<!doctype html>
<html>
<head>
<meta charset='UTF-8'><meta name='viewport' content='width=device-width initial-scale=1'>
<link href="/css/bootstrap.min.css" rel="stylesheet">
<!-- style sheet of Vssue -->
<link rel="stylesheet" href="https://unpkg.com/vssue/dist/vssue.min.css">
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>

<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/vssue/dist/vssue.gitee.min.js"></script>
<link href='https://fonts.loli.net/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext' rel='stylesheet' type='text/css' /><style type='text/css'>html {overflow-x: initial !important;}:root { --bg-color:#ffffff; --text-color:#333333; --select-text-bg-color:#B5D6FC; --select-text-font-color:auto; --monospace:"Lucida Console",Consolas,"Courier",monospace; --title-bar-height:20px; }
.mac-os-11 { --title-bar-height:28px; }
html { font-size: 14px; background-color: var(--bg-color); color: var(--text-color); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; }
body { margin: 0px; padding: 0px; height: auto; inset: 0px; font-size: 1rem; line-height: 1.42857; overflow-x: hidden; background: inherit; tab-size: 4; }
iframe { margin: auto; }
a.url { word-break: break-all; }
a:active, a:hover { outline: 0px; }
.in-text-selection, ::selection { text-shadow: none; background: var(--select-text-bg-color); color: var(--select-text-font-color); }
#write { margin: 0px auto; height: auto; width: inherit; word-break: normal; overflow-wrap: break-word; position: relative; white-space: normal; overflow-x: visible; padding-top: 36px; }
#write.first-line-indent p { text-indent: 2em; }
#write.first-line-indent li p, #write.first-line-indent p * { text-indent: 0px; }
#write.first-line-indent li { margin-left: 2em; }
.for-image #write { padding-left: 8px; padding-right: 8px; }
body.typora-export { padding-left: 30px; padding-right: 30px; }
.typora-export .footnote-line, .typora-export li, .typora-export p { white-space: pre-wrap; }
.typora-export .task-list-item input { pointer-events: none; }
@media screen and (max-width: 500px) {
  body.typora-export { padding-left: 0px; padding-right: 0px; }
  #write { padding-left: 20px; padding-right: 20px; }
  .CodeMirror-sizer { margin-left: 0px !important; }
  .CodeMirror-gutters { display: none !important; }
}
#write li > figure:last-child { margin-bottom: 0.5rem; }
#write ol, #write ul { position: relative; }
img { max-width: 100%; vertical-align: middle; image-orientation: from-image; }
button, input, select, textarea { color: inherit; font: inherit; }
input[type="checkbox"], input[type="radio"] { line-height: normal; padding: 0px; }
*, ::after, ::before { box-sizing: border-box; }
#write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write p, #write pre { width: inherit; }
#write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write p { position: relative; }
p { line-height: inherit; }
h1, h2, h3, h4, h5, h6 { break-after: avoid-page; break-inside: avoid; orphans: 4; }
p { orphans: 4; }
h1 { font-size: 2rem; }
h2 { font-size: 1.8rem; }
h3 { font-size: 1.6rem; }
h4 { font-size: 1.4rem; }
h5 { font-size: 1.2rem; }
h6 { font-size: 1rem; }
.md-math-block, .md-rawblock, h1, h2, h3, h4, h5, h6, p { margin-top: 1rem; margin-bottom: 1rem; }
.hidden { display: none; }
.md-blockmeta { color: rgb(204, 204, 204); font-weight: 700; font-style: italic; }
a { cursor: pointer; }
sup.md-footnote { padding: 2px 4px; background-color: rgba(238, 238, 238, 0.7); color: rgb(85, 85, 85); border-radius: 4px; cursor: pointer; }
sup.md-footnote a, sup.md-footnote a:hover { color: inherit; text-transform: inherit; text-decoration: inherit; }
#write input[type="checkbox"] { cursor: pointer; width: inherit; height: inherit; }
figure { overflow-x: auto; margin: 1.2em 0px; max-width: calc(100% + 16px); padding: 0px; }
figure > table { margin: 0px; }
tr { break-inside: avoid; break-after: auto; }
thead { display: table-header-group; }
table { border-collapse: collapse; border-spacing: 0px; width: 100%; overflow: auto; break-inside: auto; text-align: left; }
table.md-table td { min-width: 32px; }
.CodeMirror-gutters { border-right: 0px; background-color: inherit; }
.CodeMirror-linenumber { user-select: none; }
.CodeMirror { text-align: left; }
.CodeMirror-placeholder { opacity: 0.3; }
.CodeMirror pre { padding: 0px 4px; }
.CodeMirror-lines { padding: 0px; }
div.hr:focus { cursor: none; }
#write pre { white-space: pre-wrap; }
#write.fences-no-line-wrapping pre { white-space: pre; }
#write pre.ty-contain-cm { white-space: normal; }
.CodeMirror-gutters { margin-right: 4px; }
.md-fences { font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; overflow: visible; white-space: pre; background: inherit; position: relative !important; }
.md-fences-adv-panel { width: 100%; margin-top: 10px; text-align: center; padding-top: 0px; padding-bottom: 8px; overflow-x: auto; }
#write .md-fences.mock-cm { white-space: pre-wrap; }
.md-fences.md-fences-with-lineno { padding-left: 0px; }
#write.fences-no-line-wrapping .md-fences.mock-cm { white-space: pre; overflow-x: auto; }
.md-fences.mock-cm.md-fences-with-lineno { padding-left: 8px; }
.CodeMirror-line, twitterwidget { break-inside: avoid; }
.footnotes { opacity: 0.8; font-size: 0.9rem; margin-top: 1em; margin-bottom: 1em; }
.footnotes + .footnotes { margin-top: 0px; }
.md-reset { margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: top; background: 0px 0px; text-decoration: none; text-shadow: none; float: none; position: static; width: auto; height: auto; white-space: nowrap; cursor: inherit; -webkit-tap-highlight-color: transparent; line-height: normal; font-weight: 400; text-align: left; box-sizing: content-box; direction: ltr; }
li div { padding-top: 0px; }
blockquote { margin: 1rem 0px; }
li .mathjax-block, li p { margin: 0.5rem 0px; }
li blockquote { margin: 1rem 0px; }
li { margin: 0px; position: relative; }
blockquote > :last-child { margin-bottom: 0px; }
blockquote > :first-child, li > :first-child { margin-top: 0px; }
.footnotes-area { color: rgb(136, 136, 136); margin-top: 0.714rem; padding-bottom: 0.143rem; white-space: normal; }
#write .footnote-line { white-space: pre-wrap; }
@media print {
  body, html { border: 1px solid transparent; height: 99%; break-after: avoid; break-before: avoid; font-variant-ligatures: no-common-ligatures; }
  #write { margin-top: 0px; padding-top: 0px; border-color: transparent !important; }
  .typora-export * { -webkit-print-color-adjust: exact; }
  .typora-export #write { break-after: avoid; }
  .typora-export #write::after { height: 0px; }
  .is-mac table { break-inside: avoid; }
  .typora-export-show-outline .typora-export-sidebar { display: none; }
}
.footnote-line { margin-top: 0.714em; font-size: 0.7em; }
a img, img a { cursor: pointer; }
pre.md-meta-block { font-size: 0.8rem; min-height: 0.8rem; white-space: pre-wrap; background: rgb(204, 204, 204); display: block; overflow-x: hidden; }
p > .md-image:only-child:not(.md-img-error) img, p > img:only-child { display: block; margin: auto; }
#write.first-line-indent p > .md-image:only-child:not(.md-img-error) img { left: -2em; position: relative; }
p > .md-image:only-child { display: inline-block; width: 100%; }
#write .MathJax_Display { margin: 0.8em 0px 0px; }
.md-math-block { width: 100%; }
.md-math-block:not(:empty)::after { display: none; }
.MathJax_ref { fill: currentcolor; }
[contenteditable="true"]:active, [contenteditable="true"]:focus, [contenteditable="false"]:active, [contenteditable="false"]:focus { outline: 0px; box-shadow: none; }
.md-task-list-item { position: relative; list-style-type: none; }
.task-list-item.md-task-list-item { padding-left: 0px; }
.md-task-list-item > input { position: absolute; top: 0px; left: 0px; margin-left: -1.2em; margin-top: calc(1em - 10px); border: none; }
.math { font-size: 1rem; }
.md-toc { min-height: 3.58rem; position: relative; font-size: 0.9rem; border-radius: 10px; }
.md-toc-content { position: relative; margin-left: 0px; }
.md-toc-content::after, .md-toc::after { display: none; }
.md-toc-item { display: block; color: rgb(65, 131, 196); }
.md-toc-item a { text-decoration: none; }
.md-toc-inner:hover { text-decoration: underline; }
.md-toc-inner { display: inline-block; cursor: pointer; }
.md-toc-h1 .md-toc-inner { margin-left: 0px; font-weight: 700; }
.md-toc-h2 .md-toc-inner { margin-left: 2em; }
.md-toc-h3 .md-toc-inner { margin-left: 4em; }
.md-toc-h4 .md-toc-inner { margin-left: 6em; }
.md-toc-h5 .md-toc-inner { margin-left: 8em; }
.md-toc-h6 .md-toc-inner { margin-left: 10em; }
@media screen and (max-width: 48em) {
  .md-toc-h3 .md-toc-inner { margin-left: 3.5em; }
  .md-toc-h4 .md-toc-inner { margin-left: 5em; }
  .md-toc-h5 .md-toc-inner { margin-left: 6.5em; }
  .md-toc-h6 .md-toc-inner { margin-left: 8em; }
}
a.md-toc-inner { font-size: inherit; font-style: inherit; font-weight: inherit; line-height: inherit; }
.footnote-line a:not(.reversefootnote) { color: inherit; }
.md-attr { display: none; }
.md-fn-count::after { content: "."; }
code, pre, samp, tt { font-family: var(--monospace); }
kbd { margin: 0px 0.1em; padding: 0.1em 0.6em; font-size: 0.8em; color: rgb(36, 39, 41); background: rgb(255, 255, 255); border: 1px solid rgb(173, 179, 185); border-radius: 3px; box-shadow: rgba(12, 13, 14, 0.2) 0px 1px 0px, rgb(255, 255, 255) 0px 0px 0px 2px inset; white-space: nowrap; vertical-align: middle; }
.md-comment { color: rgb(162, 127, 3); opacity: 0.6; font-family: var(--monospace); }
code { text-align: left; vertical-align: initial; }
a.md-print-anchor { white-space: pre !important; border-width: initial !important; border-style: none !important; border-color: initial !important; display: inline-block !important; position: absolute !important; width: 1px !important; right: 0px !important; outline: 0px !important; background: 0px 0px !important; text-decoration: initial !important; text-shadow: initial !important; }
.os-windows.monocolor-emoji .md-emoji { font-family: "Segoe UI Symbol", sans-serif; }
.md-diagram-panel > svg { max-width: 100%; }
[lang="flow"] svg, [lang="mermaid"] svg { max-width: 100%; height: auto; }
[lang="mermaid"] .node text { font-size: 1rem; }
table tr th { border-bottom: 0px; }
video { max-width: 100%; display: block; margin: 0px auto; }
iframe { max-width: 100%; width: 100%; border: none; }
.highlight td, .highlight tr { border: 0px; }
mark { background: rgb(255, 255, 0); color: rgb(0, 0, 0); }
.md-html-inline .md-plain, .md-html-inline strong, mark .md-inline-math, mark strong { color: inherit; }
.md-expand mark .md-meta { opacity: 0.3 !important; }
mark .md-meta { color: rgb(0, 0, 0); }
@media print {
  .typora-export h1, .typora-export h2, .typora-export h3, .typora-export h4, .typora-export h5, .typora-export h6 { break-inside: avoid; }
}
.md-diagram-panel .messageText { stroke: none !important; }
.md-diagram-panel .start-state { fill: var(--node-fill); }
.md-diagram-panel .edgeLabel rect { opacity: 1 !important; }
.md-fences.md-fences-math { font-size: 1em; }
.md-fences-advanced:not(.md-focus) { padding: 0px; white-space: nowrap; border: 0px; }
.md-fences-advanced:not(.md-focus) { background: inherit; }
.typora-export-show-outline .typora-export-content { max-width: 1440px; margin: auto; display: flex; flex-direction: row; }
.typora-export-sidebar { width: 300px; font-size: 0.8rem; margin-top: 80px; margin-right: 18px; }
.typora-export-show-outline #write { --webkit-flex:2; flex: 2 1 0%; }
.typora-export-sidebar .outline-content { position: fixed; top: 0px; max-height: 100%; overflow: hidden auto; padding-bottom: 30px; padding-top: 60px; width: 300px; }
@media screen and (max-width: 1024px) {
  .typora-export-sidebar, .typora-export-sidebar .outline-content { width: 240px; }
}
@media screen and (max-width: 800px) {
  .typora-export-sidebar { display: none; }
}
.outline-content li, .outline-content ul { margin-left: 0px; margin-right: 0px; padding-left: 0px; padding-right: 0px; list-style: none; }
.outline-content ul { margin-top: 0px; margin-bottom: 0px; }
.outline-content strong { font-weight: 400; }
.outline-expander { width: 1rem; height: 1.42857rem; position: relative; display: table-cell; vertical-align: middle; cursor: pointer; padding-left: 4px; }
.outline-expander::before { content: ""; position: relative; font-family: Ionicons; display: inline-block; font-size: 8px; vertical-align: middle; }
.outline-item { padding-top: 3px; padding-bottom: 3px; cursor: pointer; }
.outline-expander:hover::before { content: ""; }
.outline-h1 > .outline-item { padding-left: 0px; }
.outline-h2 > .outline-item { padding-left: 1em; }
.outline-h3 > .outline-item { padding-left: 2em; }
.outline-h4 > .outline-item { padding-left: 3em; }
.outline-h5 > .outline-item { padding-left: 4em; }
.outline-h6 > .outline-item { padding-left: 5em; }
.outline-label { cursor: pointer; display: table-cell; vertical-align: middle; text-decoration: none; color: inherit; }
.outline-label:hover { text-decoration: underline; }
.outline-item:hover { border-color: rgb(245, 245, 245); background-color: var(--item-hover-bg-color); }
.outline-item:hover { margin-left: -28px; margin-right: -28px; border-left: 28px solid transparent; border-right: 28px solid transparent; }
.outline-item-single .outline-expander::before, .outline-item-single .outline-expander:hover::before { display: none; }
.outline-item-open > .outline-item > .outline-expander::before { content: ""; }
.outline-children { display: none; }
.info-panel-tab-wrapper { display: none; }
.outline-item-open > .outline-children { display: block; }
.typora-export .outline-item { padding-top: 1px; padding-bottom: 1px; }
.typora-export .outline-item:hover { margin-right: -8px; border-right: 8px solid transparent; }
.typora-export .outline-expander::before { content: "+"; font-family: inherit; top: -1px; }
.typora-export .outline-expander:hover::before, .typora-export .outline-item-open > .outline-item > .outline-expander::before { content: "−"; }
.typora-export-collapse-outline .outline-children { display: none; }
.typora-export-collapse-outline .outline-item-open > .outline-children, .typora-export-no-collapse-outline .outline-children { display: block; }
.typora-export-no-collapse-outline .outline-expander::before { content: "" !important; }
.typora-export-show-outline .outline-item-active > .outline-item .outline-label { font-weight: 700; }
.md-inline-math-container mjx-container { zoom: 0.95; }


.CodeMirror { height: auto; }
.CodeMirror.cm-s-inner { background: inherit; }
.CodeMirror-scroll { overflow: auto hidden; z-index: 3; }
.CodeMirror-gutter-filler, .CodeMirror-scrollbar-filler { background-color: rgb(255, 255, 255); }
.CodeMirror-gutters { border-right: 1px solid rgb(221, 221, 221); background: inherit; white-space: nowrap; }
.CodeMirror-linenumber { padding: 0px 3px 0px 5px; text-align: right; color: rgb(153, 153, 153); }
.cm-s-inner .cm-keyword { color: rgb(119, 0, 136); }
.cm-s-inner .cm-atom, .cm-s-inner.cm-atom { color: rgb(34, 17, 153); }
.cm-s-inner .cm-number { color: rgb(17, 102, 68); }
.cm-s-inner .cm-def { color: rgb(0, 0, 255); }
.cm-s-inner .cm-variable { color: rgb(0, 0, 0); }
.cm-s-inner .cm-variable-2 { color: rgb(0, 85, 170); }
.cm-s-inner .cm-variable-3 { color: rgb(0, 136, 85); }
.cm-s-inner .cm-string { color: rgb(170, 17, 17); }
.cm-s-inner .cm-property { color: rgb(0, 0, 0); }
.cm-s-inner .cm-operator { color: rgb(152, 26, 26); }
.cm-s-inner .cm-comment, .cm-s-inner.cm-comment { color: rgb(170, 85, 0); }
.cm-s-inner .cm-string-2 { color: rgb(255, 85, 0); }
.cm-s-inner .cm-meta { color: rgb(85, 85, 85); }
.cm-s-inner .cm-qualifier { color: rgb(85, 85, 85); }
.cm-s-inner .cm-builtin { color: rgb(51, 0, 170); }
.cm-s-inner .cm-bracket { color: rgb(153, 153, 119); }
.cm-s-inner .cm-tag { color: rgb(17, 119, 0); }
.cm-s-inner .cm-attribute { color: rgb(0, 0, 204); }
.cm-s-inner .cm-header, .cm-s-inner.cm-header { color: rgb(0, 0, 255); }
.cm-s-inner .cm-quote, .cm-s-inner.cm-quote { color: rgb(0, 153, 0); }
.cm-s-inner .cm-hr, .cm-s-inner.cm-hr { color: rgb(153, 153, 153); }
.cm-s-inner .cm-link, .cm-s-inner.cm-link { color: rgb(0, 0, 204); }
.cm-negative { color: rgb(221, 68, 68); }
.cm-positive { color: rgb(34, 153, 34); }
.cm-header, .cm-strong { font-weight: 700; }
.cm-del { text-decoration: line-through; }
.cm-em { font-style: italic; }
.cm-link { text-decoration: underline; }
.cm-error { color: red; }
.cm-invalidchar { color: red; }
.cm-constant { color: rgb(38, 139, 210); }
.cm-defined { color: rgb(181, 137, 0); }
div.CodeMirror span.CodeMirror-matchingbracket { color: rgb(0, 255, 0); }
div.CodeMirror span.CodeMirror-nonmatchingbracket { color: rgb(255, 34, 34); }
.cm-s-inner .CodeMirror-activeline-background { background: inherit; }
.CodeMirror { position: relative; overflow: hidden; }
.CodeMirror-scroll { height: 100%; outline: 0px; position: relative; box-sizing: content-box; background: inherit; }
.CodeMirror-sizer { position: relative; }
.CodeMirror-gutter-filler, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-vscrollbar { position: absolute; z-index: 6; display: none; outline: 0px; }
.CodeMirror-vscrollbar { right: 0px; top: 0px; overflow: hidden; }
.CodeMirror-hscrollbar { bottom: 0px; left: 0px; overflow: auto hidden; }
.CodeMirror-scrollbar-filler { right: 0px; bottom: 0px; }
.CodeMirror-gutter-filler { left: 0px; bottom: 0px; }
.CodeMirror-gutters { position: absolute; left: 0px; top: 0px; padding-bottom: 10px; z-index: 3; overflow-y: hidden; }
.CodeMirror-gutter { white-space: normal; height: 100%; box-sizing: content-box; padding-bottom: 30px; margin-bottom: -32px; display: inline-block; }
.CodeMirror-gutter-wrapper { position: absolute; z-index: 4; background: 0px 0px !important; border: none !important; }
.CodeMirror-gutter-background { position: absolute; top: 0px; bottom: 0px; z-index: 4; }
.CodeMirror-gutter-elt { position: absolute; cursor: default; z-index: 4; }
.CodeMirror-lines { cursor: text; }
.CodeMirror pre { border-radius: 0px; border-width: 0px; background: 0px 0px; font-family: inherit; font-size: inherit; margin: 0px; white-space: pre; overflow-wrap: normal; color: inherit; z-index: 2; position: relative; overflow: visible; }
.CodeMirror-wrap pre { overflow-wrap: break-word; white-space: pre-wrap; word-break: normal; }
.CodeMirror-code pre { border-right: 30px solid transparent; width: fit-content; }
.CodeMirror-wrap .CodeMirror-code pre { border-right: none; width: auto; }
.CodeMirror-linebackground { position: absolute; inset: 0px; z-index: 0; }
.CodeMirror-linewidget { position: relative; z-index: 2; overflow: auto; }
.CodeMirror-wrap .CodeMirror-scroll { overflow-x: hidden; }
.CodeMirror-measure { position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden; }
.CodeMirror-measure pre { position: static; }
.CodeMirror div.CodeMirror-cursor { position: absolute; visibility: hidden; border-right: none; width: 0px; }
.CodeMirror div.CodeMirror-cursor { visibility: hidden; }
.CodeMirror-focused div.CodeMirror-cursor { visibility: inherit; }
.cm-searching { background: rgba(255, 255, 0, 0.4); }
span.cm-underlined { text-decoration: underline; }
span.cm-strikethrough { text-decoration: line-through; }
.cm-tw-syntaxerror { color: rgb(255, 255, 255); background-color: rgb(153, 0, 0); }
.cm-tw-deleted { text-decoration: line-through; }
.cm-tw-header5 { font-weight: 700; }
.cm-tw-listitem:first-child { padding-left: 10px; }
.cm-tw-box { border-style: solid; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-color: inherit; border-top-width: 0px !important; }
.cm-tw-underline { text-decoration: underline; }
@media print {
  .CodeMirror div.CodeMirror-cursor { visibility: hidden; }
}


:root {
    --side-bar-bg-color: #fafafa;
    --control-text-color: #777;
}

@include-when-export url(https://fonts.loli.net/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext);

/* open-sans-regular - latin-ext_latin */
  /* open-sans-italic - latin-ext_latin */
    /* open-sans-700 - latin-ext_latin */
    /* open-sans-700italic - latin-ext_latin */
  html {
    font-size: 16px;
    -webkit-font-smoothing: antialiased;
}

body {
    font-family: "Open Sans","Clear Sans", "Helvetica Neue", Helvetica, Arial, 'Segoe UI Emoji', sans-serif;
    color: rgb(51, 51, 51);
    line-height: 1.6;
}

#write {
    max-width: 860px;
  	margin: 0 auto;
  	padding: 30px;
    padding-bottom: 100px;
}

@media only screen and (min-width: 1400px) {
	#write {
		max-width: 1024px;
	}
}

@media only screen and (min-width: 1800px) {
	#write {
		max-width: 1200px;
	}
}

#write > ul:first-child,
#write > ol:first-child{
    margin-top: 30px;
}

a {
    color: #4183C4;
}
h1,
h2,
h3,
h4,
h5,
h6 {
    position: relative;
    margin-top: 1rem;
    margin-bottom: 1rem;
    font-weight: bold;
    line-height: 1.4;
    cursor: text;
}
h1:hover a.anchor,
h2:hover a.anchor,
h3:hover a.anchor,
h4:hover a.anchor,
h5:hover a.anchor,
h6:hover a.anchor {
    text-decoration: none;
}
h1 tt,
h1 code {
    font-size: inherit;
}
h2 tt,
h2 code {
    font-size: inherit;
}
h3 tt,
h3 code {
    font-size: inherit;
}
h4 tt,
h4 code {
    font-size: inherit;
}
h5 tt,
h5 code {
    font-size: inherit;
}
h6 tt,
h6 code {
    font-size: inherit;
}
h1 {
    font-size: 2.25em;
    line-height: 1.2;
    border-bottom: 1px solid #eee;
}
h2 {
    font-size: 1.75em;
    line-height: 1.225;
    border-bottom: 1px solid #eee;
}

/*@media print {
    .typora-export h1,
    .typora-export h2 {
        border-bottom: none;
        padding-bottom: initial;
    }

    .typora-export h1::after,
    .typora-export h2::after {
        content: "";
        display: block;
        height: 100px;
        margin-top: -96px;
        border-top: 1px solid #eee;
    }
}*/

h3 {
    font-size: 1.5em;
    line-height: 1.43;
}
h4 {
    font-size: 1.25em;
}
h5 {
    font-size: 1em;
}
h6 {
   font-size: 1em;
    color: #777;
}
p,
blockquote,
ul,
ol,
dl,
table{
    margin: 0.8em 0;
}
li>ol,
li>ul {
    margin: 0 0;
}
hr {
    height: 2px;
    padding: 0;
    margin: 16px 0;
    background-color: #e7e7e7;
    border: 0 none;
    overflow: hidden;
    box-sizing: content-box;
}

li p.first {
    display: inline-block;
}
ul,
ol {
    padding-left: 30px;
}
ul:first-child,
ol:first-child {
    margin-top: 0;
}
ul:last-child,
ol:last-child {
    margin-bottom: 0;
}
blockquote {
    border-left: 4px solid #dfe2e5;
    padding: 0 15px;
    color: #777777;
}
blockquote blockquote {
    padding-right: 0;
}
table {
    padding: 0;
    word-break: initial;
}
table tr {
    border: 1px solid #dfe2e5;
    margin: 0;
    padding: 0;
}
table tr:nth-child(2n),
thead {
    background-color: #f8f8f8;
}
table th {
    font-weight: bold;
    border: 1px solid #dfe2e5;
    border-bottom: 0;
    margin: 0;
    padding: 6px 13px;
}
table td {
    border: 1px solid #dfe2e5;
    margin: 0;
    padding: 6px 13px;
}
table th:first-child,
table td:first-child {
    margin-top: 0;
}
table th:last-child,
table td:last-child {
    margin-bottom: 0;
}

.CodeMirror-lines {
    padding-left: 4px;
}

.code-tooltip {
    box-shadow: 0 1px 1px 0 rgba(0,28,36,.3);
    border-top: 1px solid #eef2f2;
}

.md-fences,
code,
tt {
    border: 1px solid #e7eaed;
    background-color: #f8f8f8;
    border-radius: 3px;
    padding: 0;
    padding: 2px 4px 0px 4px;
    font-size: 0.9em;
}

code {
    background-color: #f3f4f4;
    padding: 0 2px 0 2px;
}

.md-fences {
    margin-bottom: 15px;
    margin-top: 15px;
    padding-top: 8px;
    padding-bottom: 6px;
}


.md-task-list-item > input {
  margin-left: -1.3em;
}

@media print {
    html {
        font-size: 13px;
    }
    table,
    pre {
        page-break-inside: avoid;
    }
    pre {
        word-wrap: break-word;
    }
}

.md-fences {
	background-color: #f8f8f8;
}
#write pre.md-meta-block {
	padding: 1rem;
    font-size: 85%;
    line-height: 1.45;
    background-color: #f7f7f7;
    border: 0;
    border-radius: 3px;
    color: #777777;
    margin-top: 0 !important;
}

.mathjax-block>.code-tooltip {
	bottom: .375rem;
}

.md-mathjax-midline {
    background: #fafafa;
}

#write>h3.md-focus:before{
	left: -1.5625rem;
	top: .375rem;
}
#write>h4.md-focus:before{
	left: -1.5625rem;
	top: .285714286rem;
}
#write>h5.md-focus:before{
	left: -1.5625rem;
	top: .285714286rem;
}
#write>h6.md-focus:before{
	left: -1.5625rem;
	top: .285714286rem;
}
.md-image>.md-meta {
    /*border: 1px solid #ddd;*/
    border-radius: 3px;
    padding: 2px 0px 0px 4px;
    font-size: 0.9em;
    color: inherit;
}

.md-tag {
    color: #a7a7a7;
    opacity: 1;
}

.md-toc { 
    margin-top:20px;
    padding-bottom:20px;
}

.sidebar-tabs {
    border-bottom: none;
}

#typora-quick-open {
    border: 1px solid #ddd;
    background-color: #f8f8f8;
}

#typora-quick-open-item {
    background-color: #FAFAFA;
    border-color: #FEFEFE #e5e5e5 #e5e5e5 #eee;
    border-style: solid;
    border-width: 1px;
}

/** focus mode */
.on-focus-mode blockquote {
    border-left-color: rgba(85, 85, 85, 0.12);
}

header, .context-menu, .megamenu-content, footer{
    font-family: "Segoe UI", "Arial", sans-serif;
}

.file-node-content:hover .file-node-icon,
.file-node-content:hover .file-node-open-state{
    visibility: visible;
}

.mac-seamless-mode #typora-sidebar {
    background-color: #fafafa;
    background-color: var(--side-bar-bg-color);
}

.md-lang {
    color: #b4654d;
}

/*.html-for-mac {
    --item-hover-bg-color: #E6F0FE;
}*/

#md-notification .btn {
    border: 0;
}

.dropdown-menu .divider {
    border-color: #e5e5e5;
    opacity: 0.4;
}

.ty-preferences .window-content {
    background-color: #fafafa;
}

.ty-preferences .nav-group-item.active {
    color: white;
    background: #999;
}

.menu-item-container a.menu-style-btn {
    background-color: #f5f8fa;
    background-image: linear-gradient( 180deg , hsla(0, 0%, 100%, 0.8), hsla(0, 0%, 100%, 0)); 
}



</style><title>C/C++基础合集：面向对象</title>
</head>
<body class='typora-export os-windows typora-export-show-outline typora-export-no-collapse-outline'><div class='typora-export-content'>
<div class="typora-export-sidebar"><div class="outline-content"><li class="outline-item-wrapper outline-h1"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#c面向对象的三种特性">C++面向对象的三种特性</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h1"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#面向对象的五大基本原则">面向对象的五大基本原则</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h1"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#c继承和组合">C++继承和组合</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h1"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#c构造函数可以是虚函数吗为什么">C++构造函数可以是虚函数吗？为什么？</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h1"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#何时将析构函数声明为私有">何时将析构函数声明为私有？</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h1"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#构造函数与析构函数的调用顺序">构造函数与析构函数的调用顺序</a></div><ul class="outline-children"><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#构造函数的实现细节">构造函数的实现细节</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#析构函数的实现细节">析构函数的实现细节</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#构造函数调用虚函数">构造函数调用虚函数</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#在类的函数中如何调用虚函数">在类的函数中如何调用虚函数</a></div><ul class="outline-children"></ul></li></ul></li><li class="outline-item-wrapper outline-h1"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#为什么要将析构函数设置为虚函数">为什么要将析构函数设置为虚函数？</a></div><ul class="outline-children"><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#为什么析构函数必须是虚函数">为什么析构函数必须是虚函数？</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#为什么c默认的析构函数不是虚函数">为什么C++默认的析构函数不是虚函数？</a></div><ul class="outline-children"></ul></li></ul></li><li class="outline-item-wrapper outline-h1"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#虚函数">虚函数</a></div><ul class="outline-children"><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#虚函数表">虚函数表</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#一般继承无虚函数覆盖）">一般继承（无虚函数覆盖）</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#一般继承有虚函数覆盖）">一般继承（有虚函数覆盖） </a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#多重继承无虚函数覆盖）">多重继承（无虚函数覆盖）</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#多重继承有虚函数覆盖）">多重继承（有虚函数覆盖）</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#安全性">安全性</a></div><ul class="outline-children"><li class="outline-item-wrapper outline-h3"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#通过父类型的指针访问子类自己的虚函数">通过父类型的指针访问子类自己的虚函数</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h3"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#访问non-public的虚函数">访问non-public的虚函数</a></div><ul class="outline-children"></ul></li></ul></li></ul></li><li class="outline-item-wrapper outline-h1"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#重载重写覆盖隐藏重定义">重载、重写(覆盖)、隐藏(重定义)？</a></div><ul class="outline-children"><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#重载">重载</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#重写覆盖">重写(覆盖)</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#隐藏重定义">隐藏(重定义)</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#重载和重写的区别">重载和重写的区别</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#隐藏和重写重载的区别">隐藏和重写，重载的区别</a></div><ul class="outline-children"></ul></li></ul></li><li class="outline-item-wrapper outline-h1"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#c继承">c++继承</a></div><ul class="outline-children"><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#内存布局">内存布局</a></div><ul class="outline-children"><li class="outline-item-wrapper outline-h3"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#c语言中struct内存布局">c语言中<code>struct</code>内存布局</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h3"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#c特征的c-struct">c++特征的c <code>struct</code></a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h3"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#单继承内存布局">单继承内存布局</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h3"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#多继承内存布局">多继承内存布局</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h3"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#虚继承的内存布局">虚继承的内存布局</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h3"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#成员变量">成员变量</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h3"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#成员函数">成员函数</a></div><ul class="outline-children"><li class="outline-item-wrapper outline-h4"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#覆盖成员函数">覆盖成员函数</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h4"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#多重继承下的虚函数">多重继承下的虚函数</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h4"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#地址点与逻辑this调整">地址点与逻辑<code>this</code>调整</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h4"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#调整块">调整块</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h4"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#虚继承下的虚函数">虚继承下的虚函数</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h4"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#特殊成员函数">特殊成员函数</a></div><ul class="outline-children"><li class="outline-item-wrapper outline-h5"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#构造函数和析构函数">构造函数和析构函数</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h5"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#虚析构函数与delete操作符">虚析构函数与<code>delete</code>操作符</a></div><ul class="outline-children"></ul></li></ul></li></ul></li><li class="outline-item-wrapper outline-h3"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#数组">数组</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h3"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#异常处理">异常处理</a></div><ul class="outline-children"></ul></li></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#继承权限">继承权限</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#多继承">多继承</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#虚继承">虚继承</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h2"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#接口继承实现继承">接口继承、实现继承</a></div><ul class="outline-children"></ul></li></ul></li><li class="outline-item-wrapper outline-h1"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#抽象基类">抽象基类</a></div><ul class="outline-children"></ul></li><li class="outline-item-wrapper outline-h1"><div class="outline-item"><span class="outline-expander"></span><a class="outline-label" href="#关于this指针的理解">关于<code>this</code>指针的理解</a></div><ul class="outline-children"></ul></li></div></div><div id='write'  class=''><p><span>关于c++面向对象相关的一些面试常考知识点，许多资料基本都来源与互联网，每一部分都标明了从何处引用，与先关链接。</span></p><div class='md-toc' mdtype='toc'><p class="md-toc-content" role="list"><span role="listitem" class="md-toc-item md-toc-h1" data-ref="n5"><a class="md-toc-inner" href="#c面向对象的三种特性">C++面向对象的三种特性</a></span><span role="listitem" class="md-toc-item md-toc-h1" data-ref="n17"><a class="md-toc-inner" href="#面向对象的五大基本原则">面向对象的五大基本原则</a></span><span role="listitem" class="md-toc-item md-toc-h1" data-ref="n38"><a class="md-toc-inner" href="#c继承和组合">C++继承和组合</a></span><span role="listitem" class="md-toc-item md-toc-h1" data-ref="n86"><a class="md-toc-inner" href="#c构造函数可以是虚函数吗为什么">C++构造函数可以是虚函数吗？为什么？</a></span><span role="listitem" class="md-toc-item md-toc-h1" data-ref="n107"><a class="md-toc-inner" href="#何时将析构函数声明为私有">何时将析构函数声明为私有？</a></span><span role="listitem" class="md-toc-item md-toc-h1" data-ref="n120"><a class="md-toc-inner" href="#构造函数与析构函数的调用顺序">构造函数与析构函数的调用顺序</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n126"><a class="md-toc-inner" href="#构造函数的实现细节">构造函数的实现细节</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n148"><a class="md-toc-inner" href="#析构函数的实现细节">析构函数的实现细节</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n166"><a class="md-toc-inner" href="#构造函数调用虚函数">构造函数调用虚函数</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n168"><a class="md-toc-inner" href="#在类的函数中如何调用虚函数">在类的函数中如何调用虚函数</a></span><span role="listitem" class="md-toc-item md-toc-h1" data-ref="n182"><a class="md-toc-inner" href="#为什么要将析构函数设置为虚函数">为什么要将析构函数设置为虚函数？</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n199"><a class="md-toc-inner" href="#为什么析构函数必须是虚函数">为什么析构函数必须是虚函数？</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n201"><a class="md-toc-inner" href="#为什么c默认的析构函数不是虚函数">为什么C++默认的析构函数不是虚函数？</a></span><span role="listitem" class="md-toc-item md-toc-h1" data-ref="n205"><a class="md-toc-inner" href="#虚函数">虚函数</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n207"><a class="md-toc-inner" href="#虚函数表">虚函数表</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n222"><a class="md-toc-inner" href="#一般继承无虚函数覆盖）">一般继承（无虚函数覆盖）</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n229"><a class="md-toc-inner" href="#一般继承有虚函数覆盖）">一般继承（有虚函数覆盖） </a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n239"><a class="md-toc-inner" href="#多重继承无虚函数覆盖）">多重继承（无虚函数覆盖）</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n247"><a class="md-toc-inner" href="#多重继承有虚函数覆盖）">多重继承（有虚函数覆盖）</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n254"><a class="md-toc-inner" href="#安全性">安全性</a></span><span role="listitem" class="md-toc-item md-toc-h3" data-ref="n255"><a class="md-toc-inner" href="#通过父类型的指针访问子类自己的虚函数">通过父类型的指针访问子类自己的虚函数</a></span><span role="listitem" class="md-toc-item md-toc-h3" data-ref="n259"><a class="md-toc-inner" href="#访问non-public的虚函数">访问non-public的虚函数</a></span><span role="listitem" class="md-toc-item md-toc-h1" data-ref="n264"><a class="md-toc-inner" href="#重载重写覆盖隐藏重定义">重载、重写(覆盖)、隐藏(重定义)？</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n265"><a class="md-toc-inner" href="#重载">重载</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n279"><a class="md-toc-inner" href="#重写覆盖">重写(覆盖)</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n295"><a class="md-toc-inner" href="#隐藏重定义">隐藏(重定义)</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n309"><a class="md-toc-inner" href="#重载和重写的区别">重载和重写的区别</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n317"><a class="md-toc-inner" href="#隐藏和重写重载的区别">隐藏和重写，重载的区别</a></span><span role="listitem" class="md-toc-item md-toc-h1" data-ref="n327"><a class="md-toc-inner" href="#c继承">c++继承</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n328"><a class="md-toc-inner" href="#内存布局">内存布局</a></span><span role="listitem" class="md-toc-item md-toc-h3" data-ref="n329"><a class="md-toc-inner" href="#c语言中struct内存布局">c语言中<code>struct</code>内存布局</a></span><span role="listitem" class="md-toc-item md-toc-h3" data-ref="n335"><a class="md-toc-inner" href="#c特征的c-struct">c++特征的c <code>struct</code></a></span><span role="listitem" class="md-toc-item md-toc-h3" data-ref="n341"><a class="md-toc-inner" href="#单继承内存布局">单继承内存布局</a></span><span role="listitem" class="md-toc-item md-toc-h3" data-ref="n348"><a class="md-toc-inner" href="#多继承内存布局">多继承内存布局</a></span><span role="listitem" class="md-toc-item md-toc-h3" data-ref="n360"><a class="md-toc-inner" href="#虚继承的内存布局">虚继承的内存布局</a></span><span role="listitem" class="md-toc-item md-toc-h3" data-ref="n387"><a class="md-toc-inner" href="#成员变量">成员变量</a></span><span role="listitem" class="md-toc-item md-toc-h3" data-ref="n430"><a class="md-toc-inner" href="#成员函数">成员函数</a></span><span role="listitem" class="md-toc-item md-toc-h4" data-ref="n437"><a class="md-toc-inner" href="#覆盖成员函数">覆盖成员函数</a></span><span role="listitem" class="md-toc-item md-toc-h4" data-ref="n451"><a class="md-toc-inner" href="#多重继承下的虚函数">多重继承下的虚函数</a></span><span role="listitem" class="md-toc-item md-toc-h4" data-ref="n461"><a class="md-toc-inner" href="#地址点与逻辑this调整">地址点与逻辑<code>this</code>调整</a></span><span role="listitem" class="md-toc-item md-toc-h4" data-ref="n467"><a class="md-toc-inner" href="#调整块">调整块</a></span><span role="listitem" class="md-toc-item md-toc-h4" data-ref="n475"><a class="md-toc-inner" href="#虚继承下的虚函数">虚继承下的虚函数</a></span><span role="listitem" class="md-toc-item md-toc-h4" data-ref="n483"><a class="md-toc-inner" href="#特殊成员函数">特殊成员函数</a></span><span role="listitem" class="md-toc-item md-toc-h5" data-ref="n485"><a class="md-toc-inner" href="#构造函数和析构函数">构造函数和析构函数</a></span><span role="listitem" class="md-toc-item md-toc-h5" data-ref="n511"><a class="md-toc-inner" href="#虚析构函数与delete操作符">虚析构函数与<code>delete</code>操作符</a></span><span role="listitem" class="md-toc-item md-toc-h3" data-ref="n526"><a class="md-toc-inner" href="#数组">数组</a></span><span role="listitem" class="md-toc-item md-toc-h3" data-ref="n536"><a class="md-toc-inner" href="#异常处理">异常处理</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n544"><a class="md-toc-inner" href="#继承权限">继承权限</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n579"><a class="md-toc-inner" href="#多继承">多继承</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n594"><a class="md-toc-inner" href="#虚继承">虚继承</a></span><span role="listitem" class="md-toc-item md-toc-h2" data-ref="n612"><a class="md-toc-inner" href="#接口继承实现继承">接口继承、实现继承</a></span><span role="listitem" class="md-toc-item md-toc-h1" data-ref="n632"><a class="md-toc-inner" href="#抽象基类">抽象基类</a></span><span role="listitem" class="md-toc-item md-toc-h1" data-ref="n636"><a class="md-toc-inner" href="#关于this指针的理解">关于<code>this</code>指针的理解</a></span></p></div><hr /><h1 id='c面向对象的三种特性'><span>C++面向对象的三种特性</span></h1><p><span>面向对象的特性：</span><strong><span>封装</span></strong><span>、</span><strong><span>继承</span></strong><span>、</span><strong><span>多态</span></strong></p><ul><li><strong><span>封装</span></strong><span>: 封装就是隐藏对象的属性和实现细节，仅对外公开接口，控制在程序中属性的读和修改的访问级别，将抽象得到的数据和行为（或功能）相结合，形成一个有机的整体，也就是将数据与操作数据的源代码进行有机的结合，形成“类”，其中数据和函数都是类的成员。封装的目的是增强安全性和简化编程，使用者不必了解具体的实现细节，而只是要通过外部接口，以特定的访问权限来使用类的成员。面相对象的不就是使用程序处理事情时以对象为中心去分析吗，与面向过程不同，面向过程关心处理的逻辑、流程等问题，而不关心事件主体。而面向对象即面向主体，所以我们在解决问题时应该先进行对象的封装。</span></li><li><strong><span>继承</span></strong><span>: 继承是面向对象的基本特征之一，继承机制允许创建分等级层次的类。继承就是子类继承父类的特征和行为，使得子类对象（实例）具有父类的实例域和方法，或子类从父类继承方法，使得子类具有父类相同的行为。继承之间是子父类的关系，继承机制可以很好的描述一个类的生态，也提高了代码复用率。</span></li><li><strong><span>多态</span></strong><span>: 多态同一个行为具有多个不同表现形式或形态的能力。是指一个类实例（对象）的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着，虽然针对不同对象的具体操作不同，但通过一个公共的类，它们（那些操作）可以通过相同的方式予以调用。</span></li></ul><p><strong><span>多态的优点</span></strong><span>: (1) 消除类型之间的耦合关系 (2) 可替换性 (3) 可扩充性 (4) 接口性 (5) 灵活性 (6) 简化性。</span><strong><span>多态的必要条件</span></strong><span>：(1) 继承 (2) 重写(子类继承父类后对父类的方法进行重新定义) (3) 父类引用指向子类对象。简言之，多态其实是在继承的基础上的</span></p><p><span>以上参考</span><a href='https://www.cnblogs.com/fzz9/p/8973315.html'><span>面向对象的三大基本特征，五大基本原则</span></a></p><hr /><h1 id='面向对象的五大基本原则'><span>面向对象的五大基本原则</span></h1><ol start='' ><li><p><span>单一职责原则（SRP）</span></p><p><span>一个类应该有且只有一个去改变它的理由，这意味着一个类应该只有一项工作。</span></p><p><span>比如在职员类里，将工程师、销售人员、销售经理这些情况都放在职员类里考虑，其结果将会非常混乱，在这个假设下，职员类里的每个方法都要</span><code>if else</code><span>判断是哪种情况，从类结构上来说将会十分臃肿。</span></p></li><li><p><span>开放封闭原则（OCP）</span></p><p><span>对象或实体应该对扩展开放，对修改封闭。</span></p><p><span>更改封闭即是在我们对模块进行扩展时，勿需对源有程序代码和DLL进行修改或重新编译文件！这个原则对我们在设计类的时候很有帮助，坚持这个原则就必须尽量考虑接口封装，抽象机制和多态技术！</span></p></li><li><p><span>里氏替换原则（LSP）</span></p><p><span>在对象 </span><code>x</code><span> 为类型 </span><code>T</code><span> 时 </span><code>q(x)</code><span> 成立，那么当 </span><code>S</code><span> 是 </span><code>T</code><span> 的子类时，对象 </span><code>y</code><span> 为类型 </span><code>S</code><span> 时 </span><code>q(y)</code><span> 也应成立。（即对父类的调用同样适用于子类）</span></p></li><li><p><span>依赖倒置原则（DIP）</span></p><p><span>高层次的模块不应该依赖于低层次的模块，他们都应该依赖于抽象。具体实现应该依赖于抽象，而不是抽象依赖于实现。</span></p><p><span>可以这样理解，上面我举例子的时候先说了兔子和绵羊，然后才推出食草动物。但如果我们继续认识了牛、马等食草动物，我们会发现我们需要不断调整食草动物的描述，这样程序会变得僵化，所以我们不应该让子类依赖于实体，不应该让父类模块依赖于子类模块。所以我们需要将食草动物设计为抽象类，即</span><strong><span>抽象类或接口</span></strong><span>。这样下层只需要实现相应的细节而不会影响父类。</span></p></li><li><p><span>接口隔离原则（ISP）</span></p><p><span>不应强迫客户端实现一个它用不上的接口，或是说客户端不应该被迫依赖它们不使用的方法，使用多个专门的接口比使用单个接口要好的多！</span></p><p><span>比如，为了减少接口的定义，将许多类似的方法都放在一个接口中，最后会发现，维护和实现接口的时候花了太多精力，而接口所定义的操作相当于对客户端的一种承诺，这种承诺当然是越少越好，越精练越好，过多的承诺带来的就是你的大量精力和时间去维护！</span></p></li></ol><h1 id='c继承和组合'><span>C++继承和组合</span></h1><p><span>C++程序开发中，设计孤立的类比较容易，设计相互关联的类却比较难，这其中会涉及到两个概念，一个是继承（Inheritance），一个是组合（Composition）。因为二者有一定的相似性，往往令人混淆不清。类的组合和继承一样，是软件重用的重要方式。组合和继承都是有效地利用已有类的资源。但二者的概念和用法不同。</span></p><ol start='' ><li><h2 id='继承'><span>继承</span></h2></li></ol><p><span>若在逻辑上B 是一种A （is a kind of），则允许B 继承A 的功能，它们之间就是Is-A 关系。如男人（Man）是人（Human）的一种，女人（Woman）是人的一种。那么类Man 可以从类Human 派生，类Woman也可以从类Human 派生。示例程序如下：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Human</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Man</span>:<span class="cm-keyword">public</span> <span class="cm-variable">Human</span>{</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Woman</span>: <span class="cm-keyword">public</span> <span class="cm-variable">Human</span>{</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 184px;"></div><div class="CodeMirror-gutters" style="display: none; height: 184px;"></div></div></div></pre><p><span>在UML的术语中，继承关系被称为泛化（Generalization），类Man和Woman与类Human的UML关系图可描述如下： </span></p><p><span>  </span><img src="https://gitee.com/zyuegege/images/raw/master/imgs/inheritance.png" style="zoom:80%;" /></p><p><span>继承在逻辑上看起来比较简单，但在实际应用上可能遭遇意外。比如在OO界中著名的“鸵鸟不是鸟”和“圆不是椭圆”的问题。这样的问题说明了程序设计和现实世界存在逻辑差异。从生物学的角度，鸵鸟（Ostrich）是鸟（Bird）的一种，既然是Is-A的关系，类COstrich应该可以从类CBird派生。但是鸵鸟不会飞，但从CBird那里继承了接口函数fly，如下所示：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">CBird</span>{</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">virtual</span> <span class="cm-variable-3">void</span> <span class="cm-variable">fly</span>(){}</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> </span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">COstrich</span>{</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp;  ...</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 207px;"></div><div class="CodeMirror-gutters" style="display: none; height: 207px;"></div></div></div></pre><p><span>“圆不是椭圆”同样存在类似的问题，圆从椭圆类继承了无用的长短轴数据成员。所以更加严格的继承应该是：若在逻辑上B是A的一种，并且A的所有功能和属性对B都有意义，则允许B继承A的所有功能和属性。</span></p><p><span>类继承允许我们根据自己的实现来覆盖重写父类的实现细节，父类的实现对于子类是可见的，所以我们一般称之为</span><strong><span>白盒复用</span></strong><span>。继承易于修改或扩展那些被复用的实现，但它这种白盒复用却容易破坏封装性。因为这会将父类的实现细节暴露给子类。</span></p><ol start='2' ><li><h2 id='组合'><span>组合</span></h2></li></ol><p><span>若在逻辑上A 是B 的“一部分”（a part of），则不允许B 继承A 的功能，而是要用A和其它东西组合出B，它们之间就是“Has-A关系”。例如眼（Eye）、鼻（Nose）、口（Mouth）、耳（Ear）是头（Head）的一部分，所以类Head 应该由类Eye、Nose、Mouth、Ear 组合而成，不是派生而成。示例程序如下：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Eye</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">{</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">　　<span class="cm-variable-3">void</span> <span class="cm-variable">Look</span>(<span class="cm-variable-3">void</span>);</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Nose</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">{</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">　　<span class="cm-variable-3">void</span> <span class="cm-variable">Smell</span>(<span class="cm-variable-3">void</span>);</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Mouth</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">{</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">　　<span class="cm-variable-3">void</span> <span class="cm-variable">Eat</span>(<span class="cm-variable-3">void</span>);</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Ear</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">{</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">　　<span class="cm-variable-3">void</span> <span class="cm-variable">Listen</span>(<span class="cm-variable-3">void</span>);</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">// 正确的设计，冗长的程序</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Head</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">{</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">　　<span class="cm-variable-3">void</span> <span class="cm-variable">Look</span>(<span class="cm-variable-3">void</span>) { <span class="cm-variable">m_eye</span>.<span class="cm-variable">Look</span>(); }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">　　<span class="cm-variable-3">void</span> <span class="cm-variable">Smell</span>(<span class="cm-variable-3">void</span>) { <span class="cm-variable">m_nose</span>.<span class="cm-variable">Smell</span>(); }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">　　<span class="cm-variable-3">void</span> <span class="cm-variable">Eat</span>(<span class="cm-variable-3">void</span>) { <span class="cm-variable">m_mouth</span>.<span class="cm-variable">Eat</span>(); }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">　　<span class="cm-variable-3">void</span> <span class="cm-variable">Listen</span>(<span class="cm-variable-3">void</span>) { <span class="cm-variable">m_ear</span>.<span class="cm-variable">Listen</span>(); }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">private</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">　　<span class="cm-variable">Eye</span> <span class="cm-variable">m_eye</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">　　<span class="cm-variable">Nose</span> <span class="cm-variable">m_nose</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">　　<span class="cm-variable">Mouth</span> <span class="cm-variable">m_mouth</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">Ear</span> <span class="cm-variable">m_ear</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 782px;"></div><div class="CodeMirror-gutters" style="display: none; height: 782px;"></div></div></div></pre><p><span>如果允许Head 从Eye、Nose、Mouth、Ear 派生而成，那么Head 将自动具有Look、Smell、Eat、Listen 这些功能：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">// 错误的设计</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Head</span> : <span class="cm-keyword">public</span> <span class="cm-variable">Eye</span>, <span class="cm-keyword">public</span> <span class="cm-variable">Nose</span>, <span class="cm-keyword">public</span> <span class="cm-variable">Mouth</span>, <span class="cm-keyword">public</span> <span class="cm-variable">Ear</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 69px;"></div><div class="CodeMirror-gutters" style="display: none; height: 69px;"></div></div></div></pre><p><span>上述程序十分简短并且运行正确，但是这种设计却是错误的。所以我们要经的起“继承”的诱惑，避免犯下设计错误。在UML中，上面类的UML关系图可描述如下： </span></p><p><span> </span><img src="https://gitee.com/zyuegege/images/raw/master/imgs/composition.png" style="zoom:80%;" /></p><p><span>实心菱形代表了一种坚固的关系，被包含类的生命周期受包含类控制，被包含类会随着包含类创建而创建，消亡而消亡。组合属于</span><strong><span>黑盒复用</span></strong><span>，被包含对象的内部细节对外是不可见的，所以它的封装性相对较好，实现上相互依赖比较小，并且可以通过获取其它具有相同类型的对象引用或指针，在运行期间动态的定义组合。而缺点就是致使系统中的对象过多。</span></p><p><span>综上所述，Is-A关系用继承表示，Has-A关系用组合表示，GoF在《设计模式》中指出OO设计的一大原则就是：</span><strong><span>优先使用对象组合，而不是类继承。</span></strong></p><ol start='3' ><li><h1 id='解决圆不是椭圆继承问题杜绝不良继承'><span>解决“圆不是椭圆”继承问题，杜绝不良继承</span></h1></li></ol><p><span>封装、继承、多态是面向对象技术的三大机制，封装是基础、继承是关键、多态是延伸。继承是作为关键的一部分，如果我们理解不够深刻，则容易造成程序设计中的不良继承，影响程序质量。上文中“圆不是椭圆”这一著名问题，实际上在数学上圆是一种特殊的椭圆，于是会出现下面的继承：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">CEllipse</span>{</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">setSize</span>(<span class="cm-variable-3">float</span> <span class="cm-variable">x</span>,<span class="cm-variable-3">float</span> <span class="cm-variable">y</span>){}</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> </span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">CCircle</span>:<span class="cm-keyword">public</span> <span class="cm-variable">CEllipse</span>{};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 138px;"></div><div class="CodeMirror-gutters" style="display: none; height: 138px;"></div></div></div></pre><p><span>椭圆存在一个设置长短轴的成员函数setSize，而圆则不需要。椭圆能做某些圆不能做的事，所以圆继承自椭圆是不合理的类设计。那么面对“圆是/不是一种椭圆”这个两难的问题，我们如何解决。主要有几下几种方法： </span></p><p><span>（1）使用代码技巧来弥补设计缺陷。在子类CCircle中重新定义setSize抛出异常，或终止程序，或做其他的异常处理，但这些技巧会让用户吃惊不已，违背了接口设计的“最小惊讶原则”； </span>
<span>（2）改变观点，人为圆是不对称的。这对于我们思维严谨的程序员来说，有点不可接受； </span>
<span>（3）将基类的成员函数setSize删除。但这回影响椭圆对象的正常使用。 </span>
<span>（4）去掉它们之间的继承关系。推荐做法，既然圆继承椭圆是一种不良类设计，我们就应该杜绝。去掉继承关系，并不代表圆与椭圆就没有关系，两个类可以继承自同一个类COvalShape，不过该类不能执行不对称的setSize计算，如下图所示：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">COvalShape</span>{</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">setSize</span>(<span class="cm-variable-3">float</span> <span class="cm-variable">x</span>);</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> </span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> &nbsp;<span class="cm-def">CEllipse</span>:<span class="cm-keyword">public</span> <span class="cm-variable">COvalShape</span>{</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">setSize</span>(<span class="cm-variable-3">float</span> <span class="cm-variable">x</span>,<span class="cm-variable-3">float</span> <span class="cm-variable">y</span>);</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> </span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">CCircle</span>:<span class="cm-keyword">public</span> <span class="cm-variable">COvalShape</span>{</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 276px;"></div><div class="CodeMirror-gutters" style="display: none; height: 276px;"></div></div></div></pre><p><span>其中，椭圆增加了特有的setSize(float x,float y)运算。</span></p><p><span>不良继承出现的根本原因在于对继承的理解不够深刻，错把直觉中的“是一种（Is-A）”当成了学术中的“子类型（subtype）”概念。在继承体系中，派生类对象是可以取代基类对象的。而在椭圆和圆的问题上，椭圆类中的成员函数setSize(x,y)违背了这个可置换性，即Liskov替换原则。</span></p><p><span>所有不良继承都可以归结为“圆不是椭圆”这一著名具有代表性的问题上。在不良继承中，基类总会有一些额外能力，而派生类却无法满足它。这些额外的能力通常表现为一个或多个成员函数提供的功能。要解决这一问题，要么使基类弱化，要么消除继承关系，需要根据具体情形来选择。</span></p><p><span>[1,2,3]以上引用</span><a href='https://blog.csdn.net/qq_22080999/article/details/82048108'><span>C++继承和组合</span></a></p><hr /><ol start='4' ><li><h2 id='什么时候用继承什么时候用组合'><span>什么时候用继承？什么时候用组合？</span></h2></li></ol><ul><li><span>如果二者间存在一个</span><strong><span>“是”</span></strong><span>的关系，</span><strong><span>并且一个类要对另外一个类公开所有接口</span></strong><span>，那么继承是更好的选择</span></li><li><span>如果二者间存在一个</span><strong><span>“有”</span></strong><span>的关系，那么首选组合</span></li></ul><p><strong><span>没有找到极其强烈无法辩驳的使用继承的利用的时候，一律采用组合</span></strong><span>，组合体现为现实层面，继承主要体现在扩展方面，</span><strong><span>如果并不是需要一个类的所有东西（包括接口和熟悉），那么就不需要使用继承，使用组合更好</span></strong><span>，</span><strong><span>如果使用继承，那么必须所有的都继承，如果有的东西你不需要继承但是你继承了，那么这就是滥用继承</span></strong><span>。</span></p><p><span>以上引用</span><a href='https://www.cnblogs.com/yinbiao/p/11534037.html'><span>【C++】继承和组合的概念？什么时候用继承？什么时候用组合？</span></a></p><hr /><h1 id='c构造函数可以是虚函数吗为什么'><span>C++构造函数可以是虚函数吗？为什么？</span></h1><p><span>构造函数不能是虚函数。</span></p><ol start='' ><li><span>存储的角度：构造函数不能是虚函数。因为虚函数是由一个虚函数列表维护的，而虚函数表是存放在对象中的，如果一个构造函数是虚函数，这个虚函数列表还没有被创建出来，所以是有问题的。</span></li><li><span>使用的角度：父类和子类出现同名虚函数称为覆盖   父类指针=new 子类名(...);父类指针-&gt;函数名(...);//调用子类的虚函数。而构造函数是创建对象时自动调用的， 不是通过父类的指针或引用调用的。</span></li><li><span>对析构函数的影响：如果构造函数是虚函数，不能明确的确定其对象，如果析构函数不是虚函数，则无法确定其调用的正确性。</span></li><li><span>构造函数一个生命周期只执行一次，不是对象的动态行为，不需要为虚函数。</span></li><li><span>虚函数指针的状态是最后一个调用的函数决定的，如果构造函数设置为虚函数，则一定是调用自己的虚表中构造函数。</span></li></ol><p><span>以上引用</span><a href='https://blog.csdn.net/sinat_34382237/article/details/79295849'><span>C++构造函数能否是虚函数的问题</span></a></p><ol start='' ><li><strong><span>虚函数对应一个vtable，这大家都知道，可是这个vtable其实是存储在对象的内存空间的。问题出来了，如果构造函数是虚的，就需要通过 vtable来调用，可是对象还没有实例化，也就是内存空间还没有，无法找到vtable，所以构造函数不能是虚函数。</span></strong></li><li><span>构造函数不需要是虚函数，也不允许是虚函数，因为创建一个对象时我们总是要明确指定对象的类型，尽管我们可能通过实验室的基类的指针或引用去访问它。但析构却不一定，我们往往通过基类的指针来销毁对象。这时候如果析构函数不是虚函数，就不能正确识别对象类型从而不能正确调用析构函数。</span></li></ol><p><span>以上引用</span><a href='https://www.cnblogs.com/vincently/p/4754502.html'><span>【C++】构造函数不能是虚函数</span></a></p><hr /><h1 id='何时将析构函数声明为私有'><span>何时将析构函数声明为私有？</span></h1><p><span>当我们将析构函数声明为私有时，就意味着析构函数只能在类域中被调用，无法被外界（包括自己的对象）调用。 表现为：</span></p><ol start='' ><li><span>禁止用户对此类型的变量进行定义，即禁止在栈内存空间内创建此类型的对象。要创建对象，只能用 new 在堆上进行。</span></li><li><span>禁止用户在程序中使用 </span><code>delete</code><span> 删除此类型对象。对象的删除只能在类内实现，也就是说只有类的实现者才有可能实现对对象的 </span><code>delete</code><span>，用户不能随便删除对象。</span></li></ol><p><span>如果用户想删除对象的话，只能按照类的实现者提供的方法进行。</span></p><p><span>这样做的动机：比如这样一种情况，你希望在析构之前必须做一些事情，但是用你类的人并不知道，</span>
<span>那么你就可以重新写一个函数，里面把要做的事情全部做完了再调用析构函数。</span>
<span>这样人家只能调用你这个函数析构对象，从而保证了析构前一定会做你要求的动作。</span></p><p><span>除此之外：这样做还会限制继承。</span>
<span>如果一个类不打算作为基类，通常采用的方案就是将其析构函数声明为</span><code>private</code><span>。 C++11中可以使用final关键字实现。</span></p><p><span>样做之后大大限制了用户对此类的使用。一般来说不要这样做；通常这样做是用来达到特殊的目的，比如在 </span><code>singleton</code><span> 的实现上</span></p><p><span>以上参考</span><a href='https://www.cnblogs.com/ll-10/p/11277408.html'><span>何时将析构函数声明为私有？</span></a></p><hr /><h1 id='构造函数与析构函数的调用顺序'><span>构造函数与析构函数的调用顺序</span></h1><ul><li><span>构造时，先调用父类构造函数，再调用子类构造函数</span></li><li><span>析构时，先调用子类析构函数，再调用父类析构函数</span></li></ul><h2 id='构造函数的实现细节'><span>构造函数的实现细节</span></h2><ol start='' ><li><p><span>子类的构造函数执行流程：</span></p><ul><li><span>调用父类的构造函数</span></li><li><span>把子类的虚函数表地址赋值给对象的虚函数表指针</span></li><li><span>初始化子类的成员变量</span></li><li><span>执行子类构造函数体</span></li></ul></li><li><p><span>父类的构造函数执行流程：</span></p><ul><li><span>把父类的虚函数表地址赋值给对象的虚函数表指针</span></li><li><span>初始化父类的成员变量</span></li><li><span>执行父类构造函数体</span></li></ul></li></ol><h2 id='析构函数的实现细节'><span>析构函数的实现细节</span></h2><ol start='' ><li><p><span>子类的析构函数执行流程：</span></p><ul><li><span>把子类的虚函数表地址赋值给对象的虚函数表指针</span></li><li><span>调用函数子类析构函数体</span></li><li><span>调用父类的析构函数</span></li></ul></li><li><p><span>父类的析构函数执行流程：</span></p><ul><li><span>把父类的虚函数表地址赋值给对象的虚函数表指针</span></li><li><span>执行父类析构函数体</span></li></ul></li></ol><h2 id='构造函数调用虚函数'><span>构造函数调用虚函数</span></h2><p><span>在构造函数或析构函数，调用其他虚函数时，是通过call命令调用函数地址，跟普通函数调用方式一样。编译器对构造函数和析构函数做了特殊处理，但如果通过间接方式调用其他虚函数，就跟普通函数调用虚函数的方式是一样的。</span></p><h2 id='在类的函数中如何调用虚函数'><span>在类的函数中如何调用虚函数</span></h2><p><span>调用虚函数，主要是要找到虚函数表指针，以及虚函数在虚函数表中的偏移，然后计算出虚函数的地址。</span></p><ul><li><span>获取到</span><code>this</code><span>指针的地址。</span></li><li><span>通过</span><code>this</code><span>指针得到虚函数表地址，一般</span><code>this</code><span>指针就是指向虚函数表地址。</span></li><li><span>通过函数在虚函数表内的偏移量，加上虚函数表地址，计算出函数的地址。</span></li><li><span>通过</span><code>call</code><span>命令调用函数</span></li></ul><p><span>是调用的子类还是父类的虚函数，关键还是要看虚函数表指针指向的是谁的虚函数表。由于构造函数和析构函数，都会把虚函数表指针设置为当前类的虚函数表地址，因此，在构造函数和析构函数中调用的虚函数，都是调用的当前类的函数。构造函数和析构函数，调用其他虚函数时，由于虚函数表指针指向的是当前类的虚函数表，因此，调用的是当前类的函数。而这种实现，容易造成混淆和误解，所以，建议在构造函数和析构函数中应该避免直接或者间接调用其他虚函数。</span></p><p><span>以上引用</span><a href='https://zhuanlan.zhihu.com/p/104014640'><span>C++的构造函数可以调用其他虚函数吗？</span></a></p><hr /><h1 id='为什么要将析构函数设置为虚函数'><span>为什么要将析构函数设置为虚函数？</span></h1><p><span>C++中析构函数的作用:</span></p><ul><li><span>析构函数与构造函数对应，当对象结束其生命周期，如对象所在的函数已调用完毕时，系统会自动执行析构函数。</span></li><li><span>析构函数名也应与类名相同，只是在函数名前面加一个位取反符，例如</span><code>~stud( )</code><span>，以区别于构造函数。它不能带任何参数，也没有返回值（包括</span><code>void</code><span>类型）。只能有一个析构函数，不能重载。</span></li><li><span>如果用户没有编写析构函数，编译系统会自动生成一个缺省的析构函数（即使自定义了析构函数，编译器也总是会为我们合成一个析构函数，并且如果自定义了析构函数，编译器在执行时会</span><strong><span>先调用自定义的析构函数再调用合成的析构函数</span></strong><span>），它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。</span></li></ul><p><span>类析构顺序：1）派生类本身的析构函数；2）对象成员析构函数；3）基类析构函数。</span></p><p><span>考虑如下情况：</span></p><ul><li><span>（情况1）用派生类类型指针绑定派生类实例，析构的时候，不管基类析构函数是不是虚函数，都会正常析构</span></li><li><span>（情况2）用基类类型指针绑定派生类实例，析构的时候，如果基类析构函数不是虚函数，则只会析构基类，不会析构派生类对象，从而造成内存泄漏。</span></li></ul><p><span>为什么会出现这种现象呢，个人认为析构的时候如果没有虚函数的动态绑定功能，就只根据指针的类型来进行的，而不是根据指针绑定的对象来进行，所以只是调用了基类的析构函数；如果基类的析构函数是虚函数，则析构的时候就要根据指针绑定的对象来调用对应的析构函数了。</span></p><h2 id='为什么析构函数必须是虚函数'><span>为什么析构函数必须是虚函数？</span></h2><p><strong><span>将可能会被继承的父类的析构函数设置为虚函数，可以保证当我们</span><code>new</code><span>一个子类，然后使用基类指针指向该子类对象，释放基类指针时可以释放掉子类的空间，防止内存泄漏。</span></strong></p><h2 id='为什么c默认的析构函数不是虚函数'><span>为什么C++默认的析构函数不是虚函数？</span></h2><p><span>C++默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针，占用额外的内存。而对于不会被继承的类来说，其析构函数如果是虚函数，就会浪费内存。因此C++默认的析构函数不是虚函数，而是只有当需要当作父类时，设置为虚函数。</span></p><p><span>以上引用</span><a href='https://blog.csdn.net/u013340341/article/details/106323849'><span>为什么析构函数必须是虚函数？为什么C++默认的析构函数不是虚函数</span></a><span>、</span><a href='https://blog.csdn.net/weixin_30953735/article/details/82633999'><span>C++中为什么析构函数是虚函数</span></a></p><hr /><h1 id='虚函数'><span>虚函数</span></h1><p><span>虚函数的作用，用专业术语来解释就是实现</span><a href='https://baike.baidu.com/item/多态性'><span>多态性</span></a><span>（Polymorphism），多态性是将接口与实现进行分离；用形象的语言来解释就是实现以共同的方法，但因个体差异，而采用不同的策略。简而言之就是用父类型别的指针指向其子类的实例，然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”，这是一种泛型技术。所谓泛型技术，说白了就是试图使用不变的代码来实现可变的算法。比如：模板技术，RTTI技术，虚函数技术，要么是试图做到在编译时决议，要么试图做到运行时决议。</span></p><h2 id='虚函数表'><span>虚函数表</span></h2><p><span>对C++ 了解的人都应该知道虚函数（Virtual Function）是通过一张虚函数表（Virtual Table）来实现的。简称为V-Table。在这个表中，主是要一个类的虚函数的地址表，这张表解决了继承、覆盖的问题，保证其容真实反应实际的函数。这样，在有虚函数的类的实例中这个表被分配在了这个实例的内存中，所以，当我们用父类的指针来操作一个子类的时候，这张虚函数表就显得由为重要了，它就像一个地图一样，指明了实际所应该调用的函数。</span></p><p><span>这里我们着重看一下这张虚函数表。C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置（这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下）。 这意味着我们通过对象实例的地址得到这张虚函数表，然后就可以遍历其中函数指针，并调用相应的函数。</span></p><p><span>一个实际的例子：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Base</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">virtual</span> <span class="cm-variable-3">void</span> <span class="cm-variable">f</span>() { <span class="cm-variable">cout</span> <span class="cm-operator">&lt;&lt;</span> <span class="cm-string">"Base::f"</span> <span class="cm-operator">&lt;&lt;</span> <span class="cm-variable">endl</span>; }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">virtual</span> <span class="cm-variable-3">void</span> <span class="cm-variable">g</span>() { <span class="cm-variable">cout</span> <span class="cm-operator">&lt;&lt;</span> <span class="cm-string">"Base::g"</span> <span class="cm-operator">&lt;&lt;</span> <span class="cm-variable">endl</span>; }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">virtual</span> <span class="cm-variable-3">void</span> <span class="cm-variable">h</span>() { <span class="cm-variable">cout</span> <span class="cm-operator">&lt;&lt;</span> <span class="cm-string">"Base::h"</span> <span class="cm-operator">&lt;&lt;</span> <span class="cm-variable">endl</span>; }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 138px;"></div><div class="CodeMirror-gutters" style="display: none; height: 138px;"></div></div></div></pre><p><em><span>按照上面的说法，我们可以通过</span></em><code>Base</code><span>的实例来得到虚函数表。 </span><strong><span>下面是实际例程：</span></strong></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">typedef</span> <span class="cm-variable-3">void</span>(<span class="cm-operator">*</span><span class="cm-variable">Fun</span>)(<span class="cm-variable-3">void</span>); </span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">Base</span> <span class="cm-variable">b</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">Fun</span> <span class="cm-variable">pFun</span> <span class="cm-operator">=</span> <span class="cm-variable">NULL</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">cout</span> <span class="cm-operator">&lt;&lt;</span> <span class="cm-string">"虚函数表地址："</span> <span class="cm-operator">&lt;&lt;</span> (<span class="cm-variable-3">int*</span>)(<span class="cm-operator">&amp;</span><span class="cm-variable">b</span>) <span class="cm-operator">&lt;&lt;</span> <span class="cm-variable">endl</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">cout</span> <span class="cm-operator">&lt;&lt;</span> <span class="cm-string">"虚函数表 — 第一个函数地址："</span> <span class="cm-operator">&lt;&lt;</span> (<span class="cm-variable-3">int*</span>)<span class="cm-operator">*</span>(<span class="cm-variable-3">int*</span>)(<span class="cm-operator">&amp;</span><span class="cm-variable">b</span>) <span class="cm-operator">&lt;&lt;</span> <span class="cm-variable">endl</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">// Invoke the first virtual function </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pFun</span> <span class="cm-operator">=</span> (<span class="cm-variable">Fun</span>)<span class="cm-operator">*</span>((<span class="cm-variable-3">int*</span>)<span class="cm-operator">*</span>(<span class="cm-variable-3">int*</span>)(<span class="cm-operator">&amp;</span><span class="cm-variable">b</span>));</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pFun</span>();</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 184px;"></div><div class="CodeMirror-gutters" style="display: none; height: 184px;"></div></div></div></pre><p><span>实际运行经果如下：(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3)</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang=""><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang=""><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">虚函数表地址：0012FED4</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">虚函数表 — 第一个函数地址：0044F148</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">Base::f</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 69px;"></div><div class="CodeMirror-gutters" style="display: none; height: 69px;"></div></div></div></pre><p><span>通过这个示例，我们可以看到，我们可以通过强行把</span><code>&amp;b</code><span>转成</span><code>int *</code><span> ，取得虚函数表的地址，然后，再次取址就可以得到第一个虚函数的地址了，也就是</span><code>Base::f()</code><span>，这在上面的程序中得到了验证（把</span><code>int *</code><span> 强制转成了函数指针）。通过这个示例，我们就可以知道如果要调用</span><code>Base::g()</code><span>和</span><code>Base::h()</code><span>，其代码如下：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">(<span class="cm-variable">Fun</span>)<span class="cm-operator">*</span>((<span class="cm-variable-3">int*</span>)<span class="cm-operator">*</span>(<span class="cm-variable-3">int*</span>)(<span class="cm-operator">&amp;</span><span class="cm-variable">b</span>)<span class="cm-operator">+</span><span class="cm-number">0</span>); &nbsp;<span class="cm-comment">// Base::f()</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">(<span class="cm-variable">Fun</span>)<span class="cm-operator">*</span>((<span class="cm-variable-3">int*</span>)<span class="cm-operator">*</span>(<span class="cm-variable-3">int*</span>)(<span class="cm-operator">&amp;</span><span class="cm-variable">b</span>)<span class="cm-operator">+</span><span class="cm-number">1</span>); &nbsp;<span class="cm-comment">// Base::g()</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">(<span class="cm-variable">Fun</span>)<span class="cm-operator">*</span>((<span class="cm-variable-3">int*</span>)<span class="cm-operator">*</span>(<span class="cm-variable-3">int*</span>)(<span class="cm-operator">&amp;</span><span class="cm-variable">b</span>)<span class="cm-operator">+</span><span class="cm-number">2</span>); &nbsp;<span class="cm-comment">// Base::h()</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 69px;"></div><div class="CodeMirror-gutters" style="display: none; height: 69px;"></div></div></div></pre><p><span>用一张图表示：</span></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/o_vtable1.jpg" referrerpolicy="no-referrer" alt="img"></p><p><span>注意：在上面这个图中，我在虚函数表的最后多加了一个结点，这是虚函数表的结束结点，就像字符串的结束符</span><code>&#39;\0&#39;</code><span>一样，其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。在WinXP+VS2003下，这个值是</span><code>NULL</code><span>。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下，这个值是如果</span><code>1</code><span>，表示还有下一个虚函数表，如果值是</span><code>0</code><span>，表示是最后一个虚函数表。</span></p><p><span>下面，我将分别说明“无覆盖”和“有覆盖”时的虚函数表的样子。没有覆盖父类的虚函数是毫无意义的。我之所以要讲述没有覆盖的情况，主要目的是为了给一个对比。在比较之下，我们可以更加清楚地知道其内部的具体实现。</span></p><h2 id='一般继承无虚函数覆盖）'><span>一般继承（无虚函数覆盖）</span></h2><p><span>假设有如下所示的一个继承关系：</span></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/o_Drawing3.jpg" referrerpolicy="no-referrer" alt="img"></p><p><span>请注意，在这个继承关系中，子类没有重载任何父类的函数。那么，在派生类的实例中，其虚函数表如下所示</span></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/o_vtable2.JPG" referrerpolicy="no-referrer" alt="img"></p><p><span>我们可以看到下面几点：</span></p><p><span>1）虚函数按照其声明顺序放于表中。</span><br/><span>2）父类的虚函数在子类的虚函数前面。</span></p><h2 id='一般继承有虚函数覆盖）'><span>一般继承（有虚函数覆盖） </span></h2><p><span>覆盖父类的虚函数是很显然的事情，不然，虚函数就变得毫无意义。下面，我们来看一下，如果子类中有虚函数重载了父类的虚函数，会是一个什么样子？假设，我们有下面这样的一个继承关系。</span></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/o_Drawing4.jpg" referrerpolicy="no-referrer" alt="img"></p><p><span>为了让大家看到被继承过后的效果，在这个类的设计中，我只覆盖了父类的一个函数：</span><code>f()</code><span>。那么，对于派生类的实例，其虚函数表会是下面的一个样子：</span></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/o_vtable3.JPG" referrerpolicy="no-referrer" alt="img"></p><p><span>我们从表中可以看到下面几点，</span></p><p><span>1）覆盖的</span><code>f()</code><span>函数被放到了虚表中原来父类虚函数的位置。</span><br/><span>2）没有被覆盖的函数依旧。</span></p><p><span>这样，我们就可以看到对于下面这样的程序:</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">Base</span> <span class="cm-operator">*</span><span class="cm-variable">b</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">Derive</span>();</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">b</span><span class="cm-operator">-&gt;</span><span class="cm-variable">f</span>();</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 46px;"></div><div class="CodeMirror-gutters" style="display: none; height: 46px;"></div></div></div></pre><p><span>由</span><code>b</code><span>所指的内存中的虚函数表的</span><code>f()</code><span>的位置已经被</span><code>Derive::f()</code><span>函数地址所取代，于是在实际调用发生时，是</span><code>Derive::f()</code><span>被调用了。这就实现了多态。</span></p><h2 id='多重继承无虚函数覆盖）'><span>多重继承（无虚函数覆盖）</span></h2><p><span>再让我们来看看多重继承中的情况，假设有下面这样一个类的继承关系。注意：子类并没有覆盖父类的函数</span></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/o_Drawing1.jpg" referrerpolicy="no-referrer" alt="img"></p><p><span>对于子类实例中的虚函数表，是下面这个样子：</span></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/o_vtable4.JPG" referrerpolicy="no-referrer" alt="img"></p><p><span>我们可以看到：</span></p><p><span>1）  每个父类都有自己的虚表。</span><br/><span>2）  子类的成员函数被放到了第一个父类的表中。（所谓的第一个父类是按照声明顺序来判断的）</span></p><p><span>这样做就是为了解决不同的父类类型的指针指向同一个子类实例，而能够调用到实际的函数。</span></p><h2 id='多重继承有虚函数覆盖）'><span>多重继承（有虚函数覆盖）</span></h2><p><span>如果发生虚函数覆盖的情况，下图中，我们在子类中覆盖了父类的f()函数</span></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/o_Drawing2.jpg" referrerpolicy="no-referrer" alt="img"></p><p><span>下面是对于子类实例中的虚函数表的图：</span></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/o_vtable5.jpg" referrerpolicy="no-referrer" alt="img"></p><p><span>三个父类虚函数表中的</span><code>f()</code><span>的位置被替换成了子类的函数指针。这样，我们就可以任一静态类型的父类来指向子类，并调用子类的</span><code>f()</code><span>了。如：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">Derive</span> <span class="cm-variable">d</span>;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">Base1</span> <span class="cm-operator">*</span><span class="cm-variable">b1</span> <span class="cm-operator">=</span> <span class="cm-operator">&amp;</span><span class="cm-variable">d</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">Base2</span> <span class="cm-operator">*</span><span class="cm-variable">b2</span> <span class="cm-operator">=</span> <span class="cm-operator">&amp;</span><span class="cm-variable">d</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">Base3</span> <span class="cm-operator">*</span><span class="cm-variable">b3</span> <span class="cm-operator">=</span> <span class="cm-operator">&amp;</span><span class="cm-variable">d</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">b1</span><span class="cm-operator">-&gt;</span><span class="cm-variable">f</span>(); <span class="cm-comment">//Derive::f()</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">b2</span><span class="cm-operator">-&gt;</span><span class="cm-variable">f</span>(); <span class="cm-comment">//Derive::f()</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">b3</span><span class="cm-operator">-&gt;</span><span class="cm-variable">f</span>(); <span class="cm-comment">//Derive::f() </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">b1</span><span class="cm-operator">-&gt;</span><span class="cm-variable">g</span>(); <span class="cm-comment">//Base1::g()</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">b2</span><span class="cm-operator">-&gt;</span><span class="cm-variable">g</span>(); <span class="cm-comment">//Base2::g()</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">b3</span><span class="cm-operator">-&gt;</span><span class="cm-variable">g</span>(); <span class="cm-comment">//Base3::g()</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 276px;"></div><div class="CodeMirror-gutters" style="display: none; height: 276px;"></div></div></div></pre><h2 id='安全性'><span>安全性</span></h2><h3 id='通过父类型的指针访问子类自己的虚函数'><span>通过父类型的指针访问子类自己的虚函数</span></h3><p><span>子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到</span><code>Base1</code><span>的虚表中有</span><code>Derive</code><span>的虚函数，但我们根本不可能使用下面的语句来调用子类的自有虚函数：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">Base1</span> <span class="cm-operator">*</span><span class="cm-variable">b1</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">Derive</span>();</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">b1</span><span class="cm-operator">-&gt;</span><span class="cm-variable">f1</span>(); &nbsp;<span class="cm-comment">//编译出错</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 46px;"></div><div class="CodeMirror-gutters" style="display: none; height: 46px;"></div></div></div></pre><p><span>任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法，所以，这样的程序根本无法编译通过。但在运行时，我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。（关于这方面的尝试，通过阅读后面附录的代码，相信你可以做到这一点）</span></p><h3 id='访问non-public的虚函数'><span>访问non-public的虚函数</span></h3><p><span>如果父类的虚函数是</span><code>private</code><span>或是</span><code>protected</code><span>的，但这些非</span><code>public</code><span>的虚函数同样会存在于虚函数表中，所以，我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数，这是很容易做到的。例如：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Base</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">private</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">virtual</span> <span class="cm-variable-3">void</span> <span class="cm-variable">f</span>() { <span class="cm-variable">cout</span> <span class="cm-operator">&lt;&lt;</span> <span class="cm-string">"Base::f"</span> <span class="cm-operator">&lt;&lt;</span> <span class="cm-variable">endl</span>; </span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">}; </span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Derive</span> : <span class="cm-keyword">public</span> <span class="cm-variable">Base</span>{</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">typedef</span> <span class="cm-variable-3">void</span>(<span class="cm-operator">*</span><span class="cm-variable">Fun</span>)(<span class="cm-variable-3">void</span>);</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">void</span> <span class="cm-variable">main</span>() {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable">Derive</span> <span class="cm-variable">d</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable">Fun</span> &nbsp;<span class="cm-variable">pFun</span> <span class="cm-operator">=</span> (<span class="cm-variable">Fun</span>)<span class="cm-operator">*</span>((<span class="cm-variable-3">int*</span>)<span class="cm-operator">*</span>(<span class="cm-variable-3">int*</span>)(<span class="cm-operator">&amp;</span><span class="cm-variable">d</span>)<span class="cm-operator">+</span><span class="cm-number">0</span>);</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable">pFun</span>();</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">}</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 322px;"></div><div class="CodeMirror-gutters" style="display: none; height: 322px;"></div></div></div></pre><p><span>以上引用</span><a href='https://blog.csdn.net/lyztyycode/article/details/81326699'><span>c++虚函数详解（你肯定懂了）</span></a></p><hr /><h1 id='重载重写覆盖隐藏重定义'><span>重载、重写(覆盖)、隐藏(重定义)？</span></h1><h2 id='重载'><span>重载</span></h2><p><span>重载从</span><code>overload</code><span>翻译过来，是指同一可访问区内被声明的几个具有不同参数列表（参数的类型，个数，顺序不同）的同名函数，根据参数列表确定调用哪个函数，重载不关心函数返回类型。</span></p><ol start='' ><li><span>同的范围（在同一个作用域中）</span></li><li><span>函数名字相同</span></li><li><span>参数不同列表</span></li><li><span>virtual 关键字可有可无</span></li><li><span>返回类型可以不同</span></li></ol><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">int</span> <span class="cm-def">test</span>();</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">int</span> <span class="cm-def">test</span>(<span class="cm-variable-3">int</span> <span class="cm-variable">a</span>);</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">int</span> <span class="cm-def">test</span>(<span class="cm-variable-3">int</span> <span class="cm-variable">a</span>,<span class="cm-variable-3">double</span> <span class="cm-variable">b</span>);</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">int</span> <span class="cm-def">test</span>(<span class="cm-variable-3">double</span> <span class="cm-variable">a</span>,<span class="cm-variable-3">int</span> <span class="cm-variable">a</span>);</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">int</span> <span class="cm-def">test</span>(<span class="cm-variable">string</span> <span class="cm-variable">s</span>);</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 115px;"></div><div class="CodeMirror-gutters" style="display: none; height: 115px;"></div></div></div></pre><h2 id='重写覆盖'><span>重写(覆盖)</span></h2><p><span>重写翻译自</span><code>override</code><span>，是指派生类中存在重新定义的函数。其函数名，参数列表，返回值类型，所有都必须同基类中被重写的函数一致。只有函数体不同（花括号内），派生类调用时会调用派生类的重写函数，不会调用被重写函数。重写的基类中被重写的函数必须有</span><code>virtual</code><span>修饰。</span></p><ol start='' ><li><span>不在同一个作用域（分别位于派生类与基类）</span></li><li><span>函数名字相同</span></li><li><span>参数相同列表（参数个数，两个参数列表对应的类型）</span></li><li><span>基类函数必须有 </span><code>virtual</code><span> 关键字，不能有 </span><code>static</code><span>，大概是多态的原因吧...</span></li><li><span>返回值类型（或是协变），否则报错</span></li><li><span>重写函数的访问修饰符可以不同。尽管 </span><code>virtual</code><span> 是 </span><code>private</code><span> 的，派生类中重写改写为 </span><code>public</code><span>,</span><code>protected</code><span> 也是可以的</span></li></ol><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Base</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">test</span>(<span class="cm-variable-3">int</span> <span class="cm-variable">a</span>) {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">cout</span><span class="cm-operator">&lt;&lt;</span><span class="cm-string">"this is base"</span><span class="cm-operator">&lt;&lt;</span><span class="cm-variable">endl</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp;  }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Ship</span>:<span class="cm-keyword">public</span> <span class="cm-variable">Base</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">test</span>(<span class="cm-variable-3">int</span> <span class="cm-variable">a</span>) {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">cout</span><span class="cm-operator">&lt;&lt;</span><span class="cm-string">"this is Base overwrite function"</span><span class="cm-operator">&lt;&lt;</span><span class="cm-variable">endl</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp;  }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 299px;"></div><div class="CodeMirror-gutters" style="display: none; height: 299px;"></div></div></div></pre><h2 id='隐藏重定义'><span>隐藏(重定义)</span></h2><p><span>隐藏是指派生类的函数屏蔽了与其同名的基类函数。注意只要同名函数，不管参数列表是否相同，基类函数都会被隐藏。</span></p><ol start='' ><li><span>不在同一个作用域（分别位于派生类与基类）</span></li><li><span>函数名字相同</span></li><li><span>返回类型可以不同</span></li><li><span>参数不同，此时，不论有无</span><code>virtual</code><span>关键字，基类的函数将被隐藏（注意别与重载混淆）而不是被重写</span></li><li><span>参数相同，但是基类函数没有</span><code>virtual</code><span>关键字。此时，基类的函数被隐藏（注意别与覆盖混淆）</span></li></ol><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Base</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">virtual</span> <span class="cm-variable-3">void</span> <span class="cm-variable">test</span>(<span class="cm-variable-3">int</span> <span class="cm-variable">a</span>) { <span class="cm-comment">//有virtual关键字，参数列表不同</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">cout</span><span class="cm-operator">&lt;&lt;</span><span class="cm-string">"this is base there are different parameters with virtual"</span><span class="cm-operator">&lt;&lt;</span><span class="cm-variable">endl</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp;  }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">test1</span>() {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">cout</span><span class="cm-operator">&lt;&lt;</span><span class="cm-string">"this is base with the same parameters with not virtual"</span><span class="cm-operator">&lt;&lt;</span><span class="cm-variable">endl</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp;  }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp; <span class="cm-keyword">virtual</span> <span class="cm-variable-3">void</span> <span class="cm-variable">test2</span>() {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">cout</span><span class="cm-operator">&lt;&lt;</span><span class="cm-string">"this is base with the same parameters with virtual"</span><span class="cm-operator">&lt;&lt;</span><span class="cm-variable">endl</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp;  }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">Ship</span>:<span class="cm-keyword">public</span> <span class="cm-variable">Base</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">test</span>() {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">cout</span><span class="cm-operator">&lt;&lt;</span><span class="cm-string">"this is Ship there are different parameters with virtual cover"</span><span class="cm-operator">&lt;&lt;</span><span class="cm-variable">endl</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp;  }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">test1</span>() {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">cout</span><span class="cm-operator">&lt;&lt;</span><span class="cm-string">"this is Ship with the same parameters with not virtual cover"</span><span class="cm-operator">&lt;&lt;</span><span class="cm-variable">endl</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp;  }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">test2</span>() {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">cout</span><span class="cm-operator">&lt;&lt;</span><span class="cm-string">"this is Ship with the same parameters with virtual cover"</span><span class="cm-operator">&lt;&lt;</span><span class="cm-variable">endl</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp;  }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 575px;"></div><div class="CodeMirror-gutters" style="display: none; height: 575px;"></div></div></div></pre><h2 id='重载和重写的区别'><span>重载和重写的区别</span></h2><ol start='' ><li><span>范围区别：重写和被重写的函数在不同的类中，重载和被重载的函数在同一类中。</span></li><li><span>参数区别：重写与被重写的函数参数列表一定相同，重载和被重载的函数参数列表一定不同。</span></li><li><code>virtual</code><span>的区别：重写的基类必须要有</span><code>virtual</code><span>修饰，重载函数和被重载函数可以被</span><code>virtual</code><span>修饰，也可以没有。</span></li></ol><h2 id='隐藏和重写重载的区别'><span>隐藏和重写，重载的区别</span></h2><ol start='' ><li><span>与重载范围不同：隐藏函数和被隐藏函数在不同类中。</span></li><li><span>参数的区别：隐藏函数和被隐藏函数参数列表可以相同，也可以不同，但函数名一定同；当参数不同时，无论基类中的函数是否被</span><code>virtual</code><span>修饰，基类函数都是被隐藏，而不是被重写。</span></li><li><span>类成员函数重载：局部同名函数将隐藏而不是重载全局声明，不引入父类名字空间时子类的同名函数不会和父类的构成重载，静态成员函数可以和非静态成员函数构成重载。</span></li></ol><p><span>以上引用</span><a href='https://www.cnblogs.com/tianzeng/p/9775672.html'><span>c++中重载，重写，覆盖</span></a></p><hr /><h1 id='c继承'><span>c++继承</span></h1><h2 id='内存布局'><span>内存布局</span></h2><h3 id='c语言中struct内存布局'><span>c语言中</span><code>struct</code><span>内存布局</span></h3><p><span>由于C++基于C，所以C++也“基本上”兼容C。特别地，C++规范在</span><code>struct</code><span>上使用了和C相同的，简</span><strong><span>单的内存布局原则：成员变量按其被声明的顺序排列，按具体实现所规定的对齐原则在内存地址上对齐。</span></strong><span>所有的C/C++厂商都保证他们的C/C++编译器对于有效的C结构采用完全相同的布局。这里，</span><code>A</code><span>是一个简单的C结构，其成员布局和对齐方式都一目了然：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">A</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">char</span> <span class="cm-variable">c</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">int</span> <span class="cm-variable">i</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 92px;"></div><div class="CodeMirror-gutters" style="display: none; height: 92px;"></div></div></div></pre><p>&nbsp;</p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_1312877687eHH7.jpg" alt="img" style="zoom:150%;" /></p><p><span>从上图可见，</span><code>A</code><span>在内存中占有8个字节，按照声明成员的顺序，前4个字节包含一个字符（实际占用1个字节，3个字节空着，补对齐），后4个字节包含一个整数。</span><code>A</code><span>的指针就指向字符开始字节处。</span></p><h3 id='c特征的c-struct'><span>c++特征的c </span><code>struct</code></h3><p><span>当然了，C++不是复杂的C，C++本质上是面向对象的语言：包含 </span><strong><span>继承、封装，以及多态</span></strong><span> 。原始的C </span><code>struct</code><span>经过改造，成了面向对象世界的基石——类。除了成员变量外，C++类还可以封装成员函数和其他东西。然而，有趣的是，</span><strong><span>除非为了实现虚函数和虚继承引入的隐藏成员变量外，C++类实例的大小完全取决于一个类及其基类的成员变量！成员函数基本上不影响类实例的大小。</span></strong></p><p><span>这里提供的</span><code>B</code><span>是一个C </span><code>struct</code><span>，然而，该</span><code>struct</code><span>有一些C++特征：控制成员可见性的“</span><code>public</code><span>/</span><code>protected</code><span>/</span><code>private</code><span>”关键字、成员函数、静态成员，以及嵌套的类型声明。虽然看着琳琅满目，实际上，</span><strong><span>只有成员变量才占用类实例的空间</span></strong><span>。要注意的是，C++标准委员会不限制由“</span><code>public</code><span>/</span><code>protected</code><span>/</span><code>private</code><span>”关键字分开的各段在实现时的先后顺序，因此，不同的编译器实现的内存布局可能并不相同。</span><strong><span>（ 在VC++中，成员变量总是按照声明时的顺序排列）。</span></strong></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">B</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">public</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">int</span> <span class="cm-variable">bm1</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">protected</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">int</span> <span class="cm-variable">bm2</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">private</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">int</span> <span class="cm-variable">bm3</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">static</span> <span class="cm-variable-3">int</span> <span class="cm-variable">bsm</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">bf</span>();</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">static</span> <span class="cm-variable-3">void</span> <span class="cm-variable">bsf</span>();</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">typedef</span> <span class="cm-variable-3">void</span> <span class="cm-variable-3">*</span><span class="cm-variable">bpv</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-keyword">struct</span> <span class="cm-def">N</span>{};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 299px;"></div><div class="CodeMirror-gutters" style="display: none; height: 299px;"></div></div></div></pre><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_1312877781DdTd.jpg" alt="img" style="zoom:150%;" /></p><p><code>B</code><span>中，为何</span><code>static int bsm</code><span>不占用内存空间？因为它是</span><strong><span>静态成员，该数据存放在程序的数据段</span></strong><span>中，不在类实例中。</span></p><h3 id='单继承内存布局'><span>单继承内存布局</span></h3><p><span>C++ 提供继承的目的是在不同的类型之间提取共性。比如，科学家对物种进行分类，从而有种、属、纲等说法。有了这种层次结构，我们才可能将某些具备特定性质的东西归入到最合适的分类层次上，如“怀孩子的是哺乳动物”。由于这些属性可以被子类继承，所以，我们只要知道“鲸鱼、人”是哺乳动物，就可以方便地指出“鲸鱼、人都可以怀孩子”。那些特例，如鸭嘴兽（生蛋的哺乳动物），则要求我们对缺省的属性或行为进行覆盖。C++中的继承语法很简单，在子类后加上</span><code>::base</code><span>就可以了。下面的</span><code>D</code><span>继承自基类</span><code>C</code><span>。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">C</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">int</span> <span class="cm-variable">c1</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">cf</span>();</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 92px;"></div><div class="CodeMirror-gutters" style="display: none; height: 92px;"></div></div></div></pre><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_1312877846ODC4.jpg" alt="img" style="zoom:150%;" /></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">D</span>: <span class="cm-variable">C</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">int</span> <span class="cm-variable">d1</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">df</span>();</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 92px;"></div><div class="CodeMirror-gutters" style="display: none; height: 92px;"></div></div></div></pre><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_1312877924Z2Of.jpg" alt="img" style="zoom:150%;" /></p><p><span>既然派生类要保留基类的所有属性和行为，自然地，每个派生类的实例都包含了一份完整的基类实例数据。在</span><code>D</code><span>中，并不是说基类</span><code>C</code><span>的数据一定要放在</span><code>D</code><span>的数据之前，只不过这样放的话，能够保证</span><code>D</code><span>中的</span><code>C</code><span>对象地址，恰好是</span><code>D</code><span>对象地址的第一个字节。这种安排之下，</span><strong><span>有了派生类</span><code>D</code><span>的指针，要获得基类</span><code>C</code><span>的指针，就不必要计算偏移量 了。几乎所有知名的C++厂商都采用这种内存安排（基类成员在前）。在单继承类层次下，每一个新的派生类都简单地把自己的成员变量添加到基类的成员变量之后 。</span></strong><span>看看上图，</span><code>C</code><span>对象指针和</span><code>D</code><span>对象指针指向同一地址。</span></p><h3 id='多继承内存布局'><span>多继承内存布局</span></h3><p><span>大多数情况下，其实单继承就足够了。但是，C++为了我们的方便，还提供了多重继承。比如，我们有一个组织模型，其中有经理类（分任务），工人类（干活）。那么，对于一线经理类，即既要从上级经理那里领取任务干活，又要向下级工人分任务的角色来说，如何在类层次中表达呢？单继承在此就有点力不胜任。我们可以安排经理类先继承工人类，一线经理类再继承经理类，但这种层次结构错误地让经理类继承了工人类的属性和行为。反之亦然。当然，一线经理类也可以仅仅从一个类（经理类或工人类）继承，或者一个都不继承，重新声明一个或两个接口，但这样的实现弊处太多：多态不可能了；未能重用现有的接口；最严重的是，当接口变化时，必须多处维护。最合理的情况似乎是一线经理从两个地方继承属性和行为——经理类、工人类。C++就允许用多重继承来解决这样的问题：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">Manager</span> <span class="cm-comment">/*...*/</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-comment">/* ... */</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">Worker</span> <span class="cm-comment">/*...*/</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-comment">/* ... */</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">MiddleManager</span>: &nbsp;<span class="cm-variable">Manager</span>, <span class="cm-variable">Worker</span> <span class="cm-comment">/*, ...*/</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-comment">/* ... */</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 276px;"></div><div class="CodeMirror-gutters" style="display: none; height: 276px;"></div></div></div></pre><p><span>这样的继承将造成怎样的类布局呢？下面我们还是用“字母类”来举例：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">E</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">int</span> <span class="cm-variable">e1</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">ef</span>();</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 92px;"></div><div class="CodeMirror-gutters" style="display: none; height: 92px;"></div></div></div></pre><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_1312877988m266.jpg" alt="img" style="zoom:150%;" /></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">F</span>: <span class="cm-variable">C</span>, <span class="cm-variable">E</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">int</span> <span class="cm-variable">f1</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">ff</span>();</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 92px;"></div><div class="CodeMirror-gutters" style="display: none; height: 92px;"></div></div></div></pre><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_13128780325n5V.jpg" alt="img" style="zoom:150%;" /></p><p><span>结构</span><code>F</code><span>从</span><code>C</code><span>和</span><code>E</code><span>多重继承得来。与</span><strong><span>单继承相同的是，</span><code>F</code><span>实例拷贝了每个基类的所有数据。与单继承不同的是，在多重继承下，内嵌的两个基类的对象指针不可能全都与派生类对象指针相同：</span></strong></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">F</span> <span class="cm-variable">f</span>;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">// (void *)&amp;f == (void *)(C *)&amp;f;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">// (void *)&amp;f &lt; (void *)(E*)&amp;f;</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 69px;"></div><div class="CodeMirror-gutters" style="display: none; height: 69px;"></div></div></div></pre><p><span>上面那行说明</span><code>C</code><span>对象指针与</span><code>F</code><span>对象指针相同，下面那行说明</span><code>E</code><span>对象指针与</span><code>F</code><span>对象指针不同。观察类布局，可以看到</span><code>F</code><span>中内嵌的</span><code>E</code><span>对象，其指针与</span><code>F</code><span>指针并不相同。正如后文讨论强制转化和成员函数时指出的，这个偏移量会造成少量的调用开销。</span></p><p><strong><span>具体的编译器实现可以自由地选择内嵌基类和派生类的布局。VC++</span></strong><span> 按照基类的</span><strong><span>声明顺序先排列基类实例数据，最后才排列派生类数据。</span></strong><span> 当然，派生类数据本身也是按照声明顺序布局的（</span><strong><span>本规则并非一成不变</span></strong><span>，我们会看到，当一些基类有虚函数而另一些基类没有时，内存布局并非如此）。</span></p><h3 id='虚继承的内存布局'><span>虚继承的内存布局</span></h3><p><span>回到我们讨论的一线经理类例子。让我们考虑这种情况：如果经理类和工人类都继承自“雇员类”，将会发生什么？</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">Employee</span> <span class="cm-comment">/* ... */</span>{</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-comment">/* ... */</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">Manager</span>: &nbsp;<span class="cm-variable">Employee</span><span class="cm-comment">/* , ... */</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-comment">/* ... */</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">Worker</span> : <span class="cm-variable">Employee</span> <span class="cm-comment">/* , ... */</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-comment">/* ... */</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">MiddleManager</span>: &nbsp;<span class="cm-variable">Manager</span>, <span class="cm-variable">Worker</span> <span class="cm-comment">/* , ... */</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-comment">/* ... */</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 345px;"></div><div class="CodeMirror-gutters" style="display: none; height: 345px;"></div></div></div></pre><p><span>如果经理类和工人类都继承自雇员类，很自然地，它们每个类都会从雇员类获得一份数据拷贝。</span><strong><span>如 果不作特殊处理，一线经理类的实例将含有两个 雇员类实例，它们分别来自两个雇员基类 。</span></strong><span>如果雇员类成员变量不多，问题不严重；如果成员变量众多，则那份多余的拷贝将造成实例生成时的严重开销。更糟的是，这两份不同的雇员实例可能分别被修改，造成数据的不一致。因此，我们需要让经理类和工人类进行特殊的声明，说明它们愿意共享一份雇员基类实例数据。很不幸，在C++中，这种“共享继承”被称为</span><strong><span>“虚继承”</span></strong><span>，把问题搞得似乎很抽象。虚继承的语法很简单，在指定基类时加上</span><code>virtual</code><span>关键字即可。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">Employee</span> <span class="cm-comment">/* ... */</span>{</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-comment">/* ... */</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">Manager</span>: &nbsp;<span class="cm-keyword">virtual</span> <span class="cm-variable">Employee</span><span class="cm-comment">/* , ... */</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-comment">/* ... */</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">Worker</span> : <span class="cm-keyword">virtual</span> <span class="cm-variable">Employee</span> <span class="cm-comment">/* , ... */</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-comment">/* ... */</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">MiddleManager</span>: &nbsp;<span class="cm-variable">Manager</span>, <span class="cm-variable">Worker</span> <span class="cm-comment">/* , ... */</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-comment">/* ... */</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 345px;"></div><div class="CodeMirror-gutters" style="display: none; height: 345px;"></div></div></div></pre><p><span>使用虚继承，比起单继承和多重继承有更大的实现开销、调用开销。回忆一下，</span><strong><span>在单继承和多重继承的情况下，内嵌的基类实例地址比起派生类实例地址来，要么地址相同（单继承，以及多重继承的最靠左基类） ，要么地址相差一个固定偏移量（多重继承的非最靠左基类） 。 然而，当虚继承时，一般说来，派生类地址和其虚基类地址之间的偏移量是不固定的，因为如果这个派生类又被进一步继承的话，最终派生类会把共享的虚基类实例数据放到一个与上一层派生类不同的偏移量处。</span></strong><span>请看下例：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">G</span>: <span class="cm-keyword">virtual</span> <span class="cm-variable">C</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">int</span> <span class="cm-variable">g1</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">gf</span>();</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 92px;"></div><div class="CodeMirror-gutters" style="display: none; height: 92px;"></div></div></div></pre><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_13128780806dbk.jpg" alt="img" style="zoom:150%;" /></p><p><strong><code>GdGvbptrG</code><span>（In G, the displacement of G&#39;s virtual base pointer to G）意思是：在</span><code>G</code><span>中，</span><code>G</code><span>对象的指针与</span><code>G</code><span>的虚基类表指针之间的偏移量，在此可见为</span><code>0</code><span>，因为</span><code>G</code><span>对象内存布局第一项就是虚基类表指针； </span><code>GdGvbptrC</code><span>（In G, the displacement of G&#39;s virtual base pointer to C）意思是：在</span><code>G</code><span>中，</span><code>C</code><span>对象的指针与</span><code>G</code><span>的虚基类表指针之间的偏移量，在此可见为8。</span></strong></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">H</span>: <span class="cm-keyword">virtual</span> <span class="cm-variable">C</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">int</span> <span class="cm-variable">h1</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">hf</span>();</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 92px;"></div><div class="CodeMirror-gutters" style="display: none; height: 92px;"></div></div></div></pre><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_1312878118CwgC.jpg" alt="img" style="zoom:150%;" /></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">I</span>: <span class="cm-variable">G</span>, <span class="cm-variable">H</span> {</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">int</span> <span class="cm-variable">i1</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable-3">void</span> <span class="cm-variable">_if</span>();</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">}</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 92px;"></div><div class="CodeMirror-gutters" style="display: none; height: 92px;"></div></div></div></pre><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_13128781759JQM.jpg" alt="img"  /></p><p><span>暂时不追究</span><code>vbptr</code><span>成员变量从何而来。从上面这些图可以直观地看到，在</span><code>G</code><span>对象中，内嵌的</span><code>C</code><span>基类对象的数据紧跟在</span><code>G</code><span>的数据之后，在</span><code>H</code><span>对象中，内嵌的</span><code>C</code><span>基类对象的数据也紧跟在</span><code>H</code><span>的数据之后。但是，在</span><code>I</code><span>对象中，内存布局就并非如此了。VC++实现的内存布局中，</span><code>G</code><span>对象实例中</span><code>G</code><span>对象和</span><code>C</code><span>对象之间的偏移，不同于</span><code>I</code><span>对象实例中</span><code>G</code><span>对象和</span><code>C</code><span>对象之间的偏移。当使用指针访问虚基类成员变量时，由于指针可以是指向派生类实例的基类指针，所以，编译器不能根据声明的指针类型计算偏移，而必须找到另一种间接的方法，从派生类指针计算虚基类的位置。</span></p><p><strong><span>在VC++ 中，对每个继承自虚基类的类实例，将增加一个隐藏的</span><span>*</span><span>“虚基类表指针”（vbptr）</span><span>*</span><span>成员变量，从而达到间接计算虚基类位置的目的。该变量指向一个全类共享的偏移量表，表中项目记录了对于该类 而言，“虚基类表指针”与虚基类之间的偏移量。</span></strong>
<span>其它的实现方式中，有一种是在派生类中使用指针成员变量。这些指针成员变量指向派生类的虚基类，每个虚基类一个指针。这种方式的优点是：获取虚基类地址时，所用代码比较少。然而，编译器优化代码时通常都可以采取措施避免重复计算虚基类地址。况且，这种实现方式还有一个大弊端：从多个虚基类派生时，类实例将占用更多的内存空间；获取虚基类的虚基类的地址时，需要多次使用指针，从而效率较低等等。</span></p><p><span>在VC++中，</span><code>G</code><span>拥有一个隐藏的“虚基类表指针”成员，指向一个虚基类表，该表的第二项是</span><strong><code>GdGvbptrC</code><span>。（在</span><code>G</code><span>中，虚基类对象</span><code>C</code><span>的地址与</span><code>G</code><span>的“虚基类表指针”之间的偏移量</span></strong><span>（ 当对于所有的派生类来说偏移量不变时，省略“d”前的前缀））。比如，在32位平台上，</span><code>GdGvptrC</code><span>是8个字节。同样，在</span><code>I</code><span>实例中的</span><code>G</code><span>对象实例也有 “虚基类表指针”，不过该指针指向一个适用于“</span><strong><code>G</code><span>处于</span><code>I</code><span>之中”</span></strong><span>的虚基类表，表中一项为</span><code>IdGvbptrC</code><span>，值为20。</span></p><p><span>观察前面的</span><code>G</code><span>、</span><code>H</code><span>和</span><code>I</code><span>， 我们可以得到如下关于VC++虚继承下内存布局的结论：</span></p><ol start='' ><li><span>首先排列非虚继承的基类实例；</span></li><li><span>有虚基类时，为每个基类增加一个隐藏的</span><code>vbptr</code><span>，除非已经从非虚继承的类那里继承了一个</span><code>vbptr</code><span>；</span></li><li><span>排列派生类的新数据成员；</span></li><li><span>在实例最后，排列每个虚基类的一个实例。</span></li></ol><p><strong><span>该布局安排使得虚基类的位置随着派生类的不同而“浮动不定”，但是，非虚基类因此也就凑在一起，彼此的偏移量固定不变。</span></strong></p><h3 id='成员变量'><span>成员变量</span></h3><p><span>介绍了类布局之后，我们接着考虑对不同的继承方式，访问成员变量的开销究竟如何。</span></p><ol start='' ><li><strong><span>没有继承：</span></strong><span> 没有任何继承关系时，访问成员变量和</span><code>C</code><span>语言的情况完全一样：从指向对象的指针，考虑一定的偏移量即可。</span></li></ol><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">C</span> <span class="cm-operator">*</span> <span class="cm-variable">pc</span>;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pc</span><span class="cm-operator">-&gt;</span><span class="cm-variable">c1</span>; <span class="cm-comment">// *(pc + dCc1) // pc是指向C的指针</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 46px;"></div><div class="CodeMirror-gutters" style="display: none; height: 46px;"></div></div></div></pre><p><span>访问</span><code>C</code><span>的成员变量</span><code>c1</code><span>，只需要在</span><code>pc</code><span>上加上固定的偏移量</span><code>dCc1</code><span>（在</span><code>C</code><span>中，</span><code>C</code><span>指针地址与其</span><code>c1</code><span>成员变量之间的偏移量值），再获取该指针的内容即可。</span></p><ol start='2' ><li><strong><span>单继承：</span></strong><span> 由于派生类实例与其基类实例之间的偏移量是常数0，所以，可以直接利用基类指针和基类成员之间的偏移量关系，如此计算得以简化。</span></li></ol><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">D</span><span class="cm-operator">*</span> <span class="cm-variable">pd</span>; &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pd</span><span class="cm-operator">-&gt;</span><span class="cm-variable">c1</span>; <span class="cm-comment">// *(pd + dDC + dCc1); // *(pd + dDc1); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pd</span><span class="cm-operator">-&gt;</span><span class="cm-variable">d1</span>; <span class="cm-comment">// *(pd + dDd1); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">// D从C单继承，pd为指向D的指针。</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 92px;"></div><div class="CodeMirror-gutters" style="display: none; height: 92px;"></div></div></div></pre><p><span>当访问基类成员</span><code>c1</code><span>时，计算步骤本来应该为</span><code>pd+dDC+dCc1</code><span>，即为先计算</span><code>D</code><span>对象和</span><code>C</code><span>对象之间的偏移，再在此基础上加上</span><code>C</code><span>对象指针与成员变量</span><code>c1</code><span> 之间的偏移量。然而，由于</span><code>dDC</code><span>恒定为0，所以直接计算</span><code>C</code><span>对象地址与</span><code>c1</code><span>之间的偏移就可以了。当访问派生类成员d1时，直接计算偏移量。</span></p><ol start='3' ><li><strong><span>多重继承</span></strong><span> ：虽然派生类与某个基类之间的偏移量可能不为0，然而，该偏移量总是一个常数。只要是个常数，访问成员变量，计算成员变量偏移时的计算就可以被简化。可见即使对于多重继承来说，访问成员变量开销仍然不大。</span></li></ol><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">F</span><span class="cm-operator">*</span> <span class="cm-variable">pf</span>; &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pf</span><span class="cm-operator">-&gt;</span><span class="cm-variable">c1</span>; <span class="cm-comment">// *(pf + dFC + dCc1); // *(pf + dFc1); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pf</span><span class="cm-operator">-&gt;</span><span class="cm-variable">e1</span>; <span class="cm-comment">// *(pf + dFE + dEe1); // *(pf + dFe1); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pf</span><span class="cm-operator">-&gt;</span><span class="cm-variable">f1</span>; <span class="cm-comment">// *(pf + dFf1); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">// F继承自C和E，pf是指向F对象的指针。</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 115px;"></div><div class="CodeMirror-gutters" style="display: none; height: 115px;"></div></div></div></pre><p><span>a. 访问</span><code>C</code><span>类成员</span><code>c1</code><span>时，</span><code>F</code><span>对象与内嵌</span><code>C</code><span>对象的相对偏移为0，可以直接计算</span><code>F</code><span>和</span><code>c1</code><span>的偏移；</span>
<span>b. 访问</span><code>E</code><span>类成员</span><code>e1</code><span>时，</span><code>F</code><span>对象与内嵌E对象的相对偏移是一个常数，</span><code>F</code><span>和</span><code>e1</code><span>之间的偏移计算也可以被简化；</span>
<span>c. 访问F自己的成员</span><code>f1</code><span>时，直接计算偏移量。</span></p><ol start='4' ><li><p><strong><span>虚继承： 当类有虚基类时，访问非虚基类的成员仍然是计算固定偏移量的问题。然而，访问虚基类的成员变量，开销就增大了</span></strong><span> ，因为必须经过如下步骤才能获得成员变量的地址：</span></p><ol start='' ><li><span>获取“虚基类表指针”；</span></li><li><span>获取虚基类表中某一表项的内容；</span></li></ol></li><li><p><span>把内容中指出的偏移量加到“虚基类表指针”的地址上。</span></p><p><span>然而，事情并非永远如此。正如下面访问</span><code>I</code><span>对象的</span><code>c1</code><span>成员那样，</span><strong><span>如果不是通过指针访问，而是直接通过对象实例，则派生类的布局可以在编译期间静态获得，偏移量也可以在编译时计算，因此也就不必要根据虚基类表的表项来间接计算了。</span></strong></p></li></ol><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">I</span><span class="cm-operator">*</span> <span class="cm-variable">pi</span>; &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pi</span><span class="cm-operator">-&gt;</span><span class="cm-variable">c1</span>; <span class="cm-comment">// *(pi + dIGvbptr + (*(pi+dIGvbptr))[1] + dCc1); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pi</span><span class="cm-operator">-&gt;</span><span class="cm-variable">g1</span>; <span class="cm-comment">// *(pi + dIG + dGg1); // *(pi + dIg1); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pi</span><span class="cm-operator">-&gt;</span><span class="cm-variable">h1</span>; <span class="cm-comment">// *(pi + dIH + dHh1); // *(pi + dIh1); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pi</span><span class="cm-operator">-&gt;</span><span class="cm-variable">i1</span>; <span class="cm-comment">// *(pi + dIi1); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">I</span> <span class="cm-variable">i</span>; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">i</span>.<span class="cm-variable">c1</span>; <span class="cm-comment">// *(&amp;i + IdIC + dCc1); // *(&amp;i + IdIc1); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">// I继承自G和H，G和H的虚基类是C，pi是指向I对象的指针</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 207px;"></div><div class="CodeMirror-gutters" style="display: none; height: 207px;"></div></div></div></pre><p><span>a. 访问虚基类C的成员c1时，</span><code>dIGvbptr</code><span>是“在I中，</span><code>I</code><span>对象指针与</span><code>G</code><span>的“虚基类表指针”之间的偏移”，</span><code>*(pi + dIGvbptr)</code><span>是虚基类表的开始地址，</span><code>*(pi + dIGvbptr)[1]</code><span>是虚基类表的第二项的内容（在</span><code>I</code><span>对象中，</span><code>G</code><span>对象的“虚基类表指针”与虚基类之间的偏移），</span><code>dCc1</code><span>是</span><code>C</code><span>对象指针与成员变量</span><code>c1</code><span>之间的偏移；</span><br/><span>b. 访问非虚基类</span><code>G</code><span>的成员</span><code>g1</code><span>时，直接计算偏移量；</span><br/><span>c. 访问非虚基类</span><code>H</code><span>的成员</span><code>h1</code><span>时，直接计算偏移量；</span><br/><span>d. 访问自身成员</span><code>i1</code><span>时，直接使用偏移量；</span><br/><span>e. 当声明了一个对象实例，用点</span><code>.</code><span>操作符访问虚基类成员</span><code>c1</code><span>时，由于编译时就完全知道对象的布局情况，所以可以直接计算偏移量。</span></p><p><span>当访问类继承层次中，多层虚基类的成员变量时，情况又如何呢？比如，访问虚基类的虚基类的成员变量时？一些实现方式为：保存一个指向直接虚基类的指针，然后就可以从直接虚基类找到它的虚基类，逐级上推。VC++优化了这个过程。 VC++在虚基类表中增加了一些额外的项，这些项保存了从派生类到其各层虚基类的偏移量。</span></p><ol start='5' ><li><span>强制转化：如果没有虚基类的问题，将一个指针强制转化为另一个类型的指针代价并不高昂。如果在要求转化的两个指针之间有“基类-派生类”关系，编译器只需要简单地在两者之间加上或者减去一个偏移量即可（并且该量还往往为0）。</span></li></ol><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">F</span><span class="cm-operator">*</span> <span class="cm-variable">pf</span>; &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">(<span class="cm-variable">C</span><span class="cm-operator">*</span>)<span class="cm-variable">pf</span>; <span class="cm-comment">// (C*)(pf ? pf + dFC : 0); // (C*)pf; &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">(<span class="cm-variable">E</span><span class="cm-operator">*</span>)<span class="cm-variable">pf</span>; <span class="cm-comment">// (E*)(pf ? pf + dFE : 0); &nbsp;</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 69px;"></div><div class="CodeMirror-gutters" style="display: none; height: 69px;"></div></div></div></pre><p><span> </span><code>C</code><span>和</span><code>E</code><span>是</span><code>F</code><span>的基类，将</span><code>F</code><span>的指针</span><code>pf</code><span>转化为</span><code>C*</code><span>或</span><code>E*</code><span>，只需要将</span><code>pf</code><span>加上一个相应的偏移量。转化为</span><code>C</code><span>类型指针</span><code>C*</code><span>时，不需要计算，因为</span><code>F</code><span>和</span><code>C</code><span>之间的偏移量为 0。转化为</span><code>E</code><span>类型指针</span><code>E*</code><span>时，必须在指针上加一个非0的偏移常量</span><code>dFE</code><span>。C ++规范要求</span><code>NULL</code><span>指针在强制转化后依然为</span><code>NULL</code><span>，因此在做强制转化需要的运算之前，VC++会检查指针是否为</span><code>NULL</code><span>。当然，这个检查只有当指针被显示或者隐式转化为相关类型指针时才进行；当在派生类对象中调用基类的方法，从而派生类指针在后台被转化为一个基类的</span><code>const this</code><span> 指针时，这个检查就不需要进行了，因为在此时，该指针一定不为</span><code>NULL</code><span>。</span></p><p><span>正如你猜想的，当继承关系中存在虚基类时，强制转化的开销会比较大。具体说来，和访问虚基类成员变量的开销相当。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">I</span><span class="cm-operator">*</span> <span class="cm-variable">pi</span>; &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">(<span class="cm-variable">G</span><span class="cm-operator">*</span>)<span class="cm-variable">pi</span>; <span class="cm-comment">// (G*)pi; &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">(<span class="cm-variable">H</span><span class="cm-operator">*</span>)<span class="cm-variable">pi</span>; <span class="cm-comment">// (H*)(pi ? pi + dIH : 0); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">(<span class="cm-variable">C</span><span class="cm-operator">*</span>)<span class="cm-variable">pi</span>; <span class="cm-comment">// (C*)(pi ? (pi+dIGvbptr + (*(pi+dIGvbptr))[1]) : 0); &nbsp;</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 92px;"></div><div class="CodeMirror-gutters" style="display: none; height: 92px;"></div></div></div></pre><p><code>pi</code><span>是指向</span><code>I</code><span>对象的指针，</span><code>G</code><span>,</span><code>H</code><span>是</span><code>I</code><span>的基类，</span><code>C</code><span>是</span><code>G</code><span>,</span><code>H</code><span>的虚基类</span></p><p><span>a. 强制转化</span><code>pi</code><span>为</span><code>G*</code><span>时，由于</span><code>G*</code><span>和</span><code>I*</code><span>的地址相同，不需要计算；</span><br/><span>b. 强制转化</span><code>pi</code><span>为</span><code>H*</code><span>时，只需要考虑一个常量偏移；</span><br/><span>c. 强制转化</span><code>pi</code><span>为</span><code>C*</code><span>时，所作的计算和访问虚基类成员变量的开销相同，首先得到</span><code>G</code><span>的虚基类表指针，再从虚基类表的第二项中取出</span><code>G</code><span>到虚基类</span><code>C</code><span>的偏移量，最后根据</span><code>pi</code><span>、虚基类表偏移和虚基类</span><code>C</code><span>与虚基类表指针之间的偏移计算出</span><code>C*</code><span>。</span></p><p><strong><span>一般说来，当从派生类中访问虚基类成员时，应该先强制转化派生类指针为虚基类指针，然后一直使用虚基类指针来访问虚基类成员变量。这样做，可以避免每次都要计算虚基类地址的开销。</span></strong><span>见下例。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang=""><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang=""><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">/* before: */ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ... pi-&gt;c1 ... pi-&gt;c1 ...</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">/* faster: */ C* pc = pi; ... pc-&gt;c1 ... pc-&gt;c1 ...</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 46px;"></div><div class="CodeMirror-gutters" style="display: none; height: 46px;"></div></div></div></pre><p><span>前者一直使用派生类指针</span><code>pi</code><span>，故每次访问</span><code>c1</code><span>都有计算虚基类地址的较大开销；后者先将</span><code>pi</code><span>转化为虚基类指针</span><code>pc</code><span>，故后续调用可以省去计算虚基类地址的开销。</span></p><h3 id='成员函数'><span>成员函数</span></h3><p><span>一个C++成员函数只是类范围内的又一个成员。</span><strong><code>X</code><span>类每一个非静态的成员函数都会接受一个特殊的隐藏参数——</span><code>this</code><span>指针，类型为</span><code>X* const</code><span>。</span></strong><span>该指针在后台初始化为指向成员函数工作于其上的对象。同样，在成员函数体内，成员变量的访问是通过在后台计算与</span><code>this</code><span>指针的偏移来进行。</span></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_1312878228wGI4.jpg" alt="img" style="zoom:150%;" /></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">P</span> { &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">int</span> <span class="cm-variable">p1</span>; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">void</span> <span class="cm-variable">pf</span>(); <span class="cm-comment">// new &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-keyword">virtual</span> <span class="cm-variable-3">void</span> <span class="cm-variable">pvf</span>(); <span class="cm-comment">// new &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">}; </span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 115px;"></div><div class="CodeMirror-gutters" style="display: none; height: 115px;"></div></div></div></pre><p><code>P</code><span>有一个非虚成员函数</span><code>pf()</code><span>，以及一个虚成员函数</span><code>pvf()</code><span>。很明显，虚成员函数造成对象实例占用更多内存空间，因为虚成员函数需要虚函数表指针。这一点以后还会谈到。这里要特别指出的是，声明非虚成员函数不会造成任何对象实例的内存开销。现在，考虑</span><code>P::pf()</code><span>的定义。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">void</span> <span class="cm-def">P::pf</span>() { <span class="cm-comment">// void P::pf([P *const this]) &nbsp;</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-operator">++</span><span class="cm-variable">p1</span>; &nbsp; <span class="cm-comment">// ++(this-&gt;p1); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">} </span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 69px;"></div><div class="CodeMirror-gutters" style="display: none; height: 69px;"></div></div></div></pre><p><span>这里</span><code>P:pf()</code><span>接受了一个</span><strong><span>隐藏的</span><code>this</code><span>指针参数</span></strong><span>，对于每个成员函数调用，编译器都会自动加上这个参数。同时，注意成员变量访问也许比看起来要代价高昂一些，因为成员变量访问通过</span><code>this</code><span>指针进行，在有的继承层次下，</span><code>this</code><span>指针需要调整，所以访问的开销可能会比较大。然而，从另一方面来说，编译器通常会把</span><code>this</code><span>指针缓存到寄存器中，所以，成员变量访问的代价不会比访问局部变量的效率更差。</span>
<span>访问局部变量，需要到</span><code>SP</code><span>寄存器中得到栈指针，再加上局部变量与栈顶的偏移。在没有虚基类的情况下，如果编译器把</span><code>this</code><span>指针缓存到了寄存器中，访问成员变量的过程将与访问局部变量的开销相似。</span></p><h4 id='覆盖成员函数'><span>覆盖成员函数</span></h4><p><span>和成员变量一样，成员函数也会被继承。与成员变量不同的是，通过在派生类中重新定义基类函数，一个派生类可以覆盖，或者说替换掉基类的函数定义。</span><strong><span>覆盖是静态 （根据成员函数的静态类型在编译时决定）还是动态 （通过对象指针在运行时动态决定），依赖于成员函数是否被声明为“虚函数”。</span></strong></p><p><code>Q</code><span>从</span><code>P</code><span>继承了成员变量和成员函数。</span><code>Q</code><span>声明了</span><code>pf()</code><span>，覆盖了</span><code>P::pf()</code><span>。</span><code>Q</code><span>还声明了</span><code>pvf()</code><span>，覆盖了</span><code>P::pvf()</code><span>虚函数。</span><code>Q</code><span>还声明了新的非虚成员函数</span><code>qf()</code><span>，以及新的虚成员函数</span><code>qvf()</code><span>。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">Q</span> : <span class="cm-variable">P</span> { &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">int</span> <span class="cm-variable">q1</span>; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">void</span> <span class="cm-variable">pf</span>(); <span class="cm-comment">// overrides P::pf &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">void</span> <span class="cm-variable">qf</span>(); <span class="cm-comment">// new &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">void</span> <span class="cm-variable">pvf</span>(); <span class="cm-comment">// overrides P::pvf &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-keyword">virtual</span> <span class="cm-variable-3">void</span> <span class="cm-variable">qvf</span>(); <span class="cm-comment">// new &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">}; &nbsp;</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 161px;"></div><div class="CodeMirror-gutters" style="display: none; height: 161px;"></div></div></div></pre><p><span>对于</span><strong><span>非虚</span></strong><span> 的成员函数来说，调用哪个成员函数是在</span><strong><span>编译</span></strong><span>时，根据</span><code>-&gt;</code><span>操作符左边指针表达式的类型</span><strong><span>静态决定</span></strong><span> 的。特别地，即使</span><code>ppq</code><span>指向</span><code>Q</code><span>的实例，</span><code>ppq-&gt;pf()</code><span>仍然调用的是</span><code>P::pf()</code><span>，因为</span><code>ppq</code><span>被声明为</span><code>P*</code><span>。（注意，</span><code>-&gt;</code><span>操作符左边的指针类型决定隐藏的</span><code>this</code><span>参数的类型。）</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">P</span> <span class="cm-variable">p</span>; <span class="cm-variable">P</span><span class="cm-operator">*</span> <span class="cm-variable">pp</span> <span class="cm-operator">=</span> <span class="cm-operator">&amp;</span><span class="cm-variable">p</span>; <span class="cm-variable">Q</span> <span class="cm-variable">q</span>; <span class="cm-variable">P</span><span class="cm-operator">*</span> <span class="cm-variable">ppq</span> <span class="cm-operator">=</span> <span class="cm-operator">&amp;</span><span class="cm-variable">q</span>; <span class="cm-variable">Q</span><span class="cm-operator">*</span> <span class="cm-variable">pq</span> <span class="cm-operator">=</span> <span class="cm-operator">&amp;</span><span class="cm-variable">q</span>; &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pp</span><span class="cm-operator">-&gt;</span><span class="cm-variable">pf</span>(); <span class="cm-comment">// pp-&gt;P::pf(); // P::pf(pp); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">ppq</span><span class="cm-operator">-&gt;</span><span class="cm-variable">pf</span>(); <span class="cm-comment">// ppq-&gt;P::pf(); // P::pf(ppq); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pq</span><span class="cm-operator">-&gt;</span><span class="cm-variable">pf</span>(); <span class="cm-comment">// pq-&gt;Q::pf(); // Q::pf((P*)pq); （错误!） &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pq</span><span class="cm-operator">-&gt;</span><span class="cm-variable">qf</span>(); <span class="cm-comment">// pq-&gt;Q::qf(); // Q::qf(pq); &nbsp;</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 115px;"></div><div class="CodeMirror-gutters" style="display: none; height: 115px;"></div></div></div></pre><p><span>标记“错误”处，</span><code>P*</code><span>似应为</span><code>Q*</code><span>。因为</span><code>pf</code><span>非虚函数，而</span><code>pq</code><span>的类型为</span><code>Q*</code><span>，故应该调用到</span><code>Q</code><span>的</span><code>pf</code><span>函数上，从而该函数应该要求一个</span><code>Q* const</code><span>类型的</span><code>this</code><span>指针。</span></p><p><span>对于</span><strong><span>虚函数</span></strong><span>调用来说，调用哪个成员函数在</span><strong><span>运行时</span></strong><span> 决定。不管</span><code>-&gt;</code><span>操作符左边的指针表达式的类型如何，调用的虚函数都是</span><strong><span>由指针实际指向的实例类型所决定</span></strong><span>。比如，尽管</span><code>ppq</code><span>的类型是</span><code>P*</code><span>，当</span><code>ppq</code><span>指向</span><code>Q</code><span>的实例时，调用的仍然是</span><code>Q::pvf()</code><span>。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pp</span><span class="cm-operator">-&gt;</span><span class="cm-variable">pvf</span>(); <span class="cm-comment">// pp-&gt;P::pvf(); // P::pvf(pp); &nbsp;</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">ppq</span><span class="cm-operator">-&gt;</span><span class="cm-variable">pvf</span>(); <span class="cm-comment">// ppq-&gt;Q::pvf(); // Q::pvf((Q*)ppq); &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pq</span><span class="cm-operator">-&gt;</span><span class="cm-variable">pvf</span>(); <span class="cm-comment">// pq-&gt;Q::pvf(); // Q::pvf((P*)pq); （错误！） &nbsp;</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 69px;"></div><div class="CodeMirror-gutters" style="display: none; height: 69px;"></div></div></div></pre><p><span>标记“错误”处，</span><code>P*</code><span>似应为</span><code>Q*</code><span>。因为</span><code>pvf</code><span>是虚函数，</span><code>pq</code><span>本来就是</span><code>Q*</code><span>，又指向</span><code>Q</code><span>的实例，从哪个方面来看都不应该是</span><code>P*</code></p><p><span>为了实现这种机制</span><strong><span>，引入了隐藏的</span><code>vfptr</code><span> 成员变量。</span></strong><span> 一个</span><code>vfptr</code><span>被加入到类中（如果类中没有的话），该</span><code>vfptr</code><span>指向类的虚函数表（</span><code>vftable</code><span>）。类中每个虚函数在该类的虚函数表中都占据一项。每项保存一个对于该类适用的虚函数的地址。因此，</span><strong><span>调用虚函数的过程如下：取得实例的</span><code>vfptr</code><span>；通过</span><code>vfptr</code><span>得到虚函数表的一项；通过虚函数表该项的函数地址间接调用虚函数。</span></strong><span>也就是说，在普通函数调用的参数传递、调用、返回指令开销外，虚函数调用还需要额外的开销。</span></p><p><span>回头再看看</span><code>P</code><span>和</span><code>Q</code><span>的内存布局，可以发现，VC++编译器把隐藏的</span><code>vfptr</code><span>成员变量放在</span><code>P</code><span>和</span><code>Q</code><span>实例的开始处。这就使虚函数的调用能够尽量快一些。实际上，</span><strong><span>VC++的实现方式是，保证任何有虚函数的类的第一项永远是</span><code>vfptr</code><span>。</span></strong><span>这就可能要求在实例布局时，在基类前插入新的</span><code>vfptr</code><span>，或者要求在多重继承时，虽然在右边，然而有</span><code>vfptr</code><span>的基类放到左边没有</span><code>vfptr</code><span>的基类的前面（如下）。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">CA</span> &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">{ &nbsp; <span class="cm-variable-3">int</span> <span class="cm-variable">a</span>;}; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">CB</span> &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">{ &nbsp; <span class="cm-variable-3">int</span> <span class="cm-variable">b</span>;}; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">CL</span> : <span class="cm-keyword">public</span> <span class="cm-variable">CB</span>, <span class="cm-keyword">public</span> <span class="cm-variable">CA</span> &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">{ &nbsp; <span class="cm-variable-3">int</span> <span class="cm-variable">c</span>;}; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">// 对于CL类，它的内存布局是：</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">int</span> <span class="cm-variable">b</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">int</span> <span class="cm-variable">a</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">int</span> <span class="cm-variable">c</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">// 但是，改造CA如下：</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">CA</span>  { &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">int</span> <span class="cm-variable">a</span>; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-keyword">virtual</span> <span class="cm-variable-3">void</span> <span class="cm-variable">seta</span>( <span class="cm-variable-3">int</span> <span class="cm-variable">_a</span> ) { <span class="cm-variable">a</span> <span class="cm-operator">=</span> <span class="cm-variable">_a</span>; } &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">}; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">// 对于同样继承顺序的CL，内存布局是：</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">vfptr</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">int</span> <span class="cm-variable">a</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">int</span> <span class="cm-variable">b</span>;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">int</span> <span class="cm-variable">c</span>;</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 529px;"></div><div class="CodeMirror-gutters" style="display: none; height: 529px;"></div></div></div></pre><p><span>许多C++的实现会共享或者重用从基类继承来的</span><code>vfptr</code><span>。比如，</span><code>Q</code><span>并不会有一个额外的</span><code>vfptr</code><span>，指向一个专门存放新的虚函数</span><code>qvf()</code><span>的虚函数表。</span><code>Qvf</code><span>项只是简单地</span><strong><span>追加</span></strong><span>到</span><code>P</code><span>的虚函数表的末尾。如此一来，单继承的代价就不算高昂。一旦一个实例有</span><code>vfptr</code><span>了，它就不需要更多的</span><code>vfptr</code><span>。新的派生类可以引入更多的虚函数，这些新的虚函数只是简单地在已存在的，“每类一个”的虚函数表的末尾追加新项。</span></p><h4 id='多重继承下的虚函数'><span>多重继承下的虚函数</span></h4><p><span>如果从多个有虚函数的基类继承，一个实例就有可能包含多个</span><code>vfptr</code><span>。考虑如下的</span><code>R</code><span>和</span><code>S</code><span>类：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">R</span> { &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">int</span> <span class="cm-variable">r1</span>; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-keyword">virtual</span> <span class="cm-variable-3">void</span> <span class="cm-variable">pvf</span>(); <span class="cm-comment">// new &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-keyword">virtual</span> <span class="cm-variable-3">void</span> <span class="cm-variable">rvf</span>(); <span class="cm-comment">// new &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">}; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">
</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">S</span> : <span class="cm-variable">P</span>, <span class="cm-variable">R</span> { &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">int</span> <span class="cm-variable">s1</span>; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">void</span> <span class="cm-variable">pvf</span>(); <span class="cm-comment">// overrides P::pvf and R::pvf &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">void</span> <span class="cm-variable">rvf</span>(); <span class="cm-comment">// overrides R::rvf &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">void</span> <span class="cm-variable">svf</span>(); <span class="cm-comment">// new &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">}; &nbsp;</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 276px;"></div><div class="CodeMirror-gutters" style="display: none; height: 276px;"></div></div></div></pre><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_1312878362w5LY.jpg" alt="img" style="zoom:150%;" /></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_1312878406xm5N.jpg" alt="img" style="zoom:120%;" /></p><p><span>这里</span><code>R</code><span>是另一个包含虚函数的类。因为</span><code>S</code><span>从</span><code>P</code><span>和</span><code>R</code><span>多重继承，</span><code>S</code><span>的实例内嵌</span><code>P</code><span>和</span><code>R</code><span>的实例，以及</span><code>S</code><span>自身的数据成员</span><code>S::s1</code><span>。注意，</span><strong><span>在多重继承下，靠右的基类</span><code>R</code><span>，其实例的地址和</span><code>P</code><span>与</span><code>S</code><span>不同。</span></strong><code>S::pvf</code><span>覆盖了</span><code>P::pvf()</code><span>和</span><code>R::pvf()</code><span>，</span><code>S::rvf()</code><span>覆盖了</span><code>R::rvf()</code><span>。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">S</span> <span class="cm-variable">s</span>; <span class="cm-variable">S</span><span class="cm-operator">*</span> <span class="cm-variable">ps</span> <span class="cm-operator">=</span> <span class="cm-operator">&amp;</span><span class="cm-variable">s</span>; &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">((<span class="cm-variable">P</span><span class="cm-operator">*</span>)<span class="cm-variable">ps</span>)<span class="cm-operator">-&gt;</span><span class="cm-variable">pvf</span>(); <span class="cm-comment">// (*(P*)ps)-&gt;P::vfptr[0])((S*)(P*)ps) &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">((<span class="cm-variable">R</span><span class="cm-operator">*</span>)<span class="cm-variable">ps</span>)<span class="cm-operator">-&gt;</span><span class="cm-variable">pvf</span>(); <span class="cm-comment">// (*(R*)ps)-&gt;R::vfptr[0])((S*)(R*)ps) &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">ps</span><span class="cm-operator">-&gt;</span><span class="cm-variable">pvf</span>(); &nbsp; &nbsp; &nbsp; <span class="cm-comment">// one of the above; calls S::pvf() &nbsp;</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 92px;"></div><div class="CodeMirror-gutters" style="display: none; height: 92px;"></div></div></div></pre><p><span>调用</span><code>((P*)ps)-&gt;pvf()</code><span>时，先到</span><code>P</code><span>的虚函数表中取出第一项，然后把</span><code>ps</code><span>转化为</span><code>S*</code><span>作为</span><code>this</code><span>指针传递进去；</span>
<span>调用</span><code>((R*)ps)-&gt;pvf()</code><span>时，先到</span><code>R</code><span>的虚函数表中取出第一项，然后把</span><code>ps</code><span>转化为</span><code>S*</code><span>作为</span><code>this</code><span>指针传递进去；</span></p><p><span>因为</span><code>S::pvf()</code><span>覆盖了</span><code>P::pvf()</code><span>和</span><code>R::pvf()</code><span>，在</span><code>S</code><span>的虚函数表中，相应的项也应该被覆盖。然而，我们很快注意到，不光可以用</span><code>P*</code><span>，还可以用</span><code>R*</code><span>来调用</span><code>pvf()</code><span>。问题出现了：</span><code>R</code><span>的地址与</span><code>P</code><span>和</span><code>S</code><span>的地址不同。表达式 </span><code>(R*)ps</code><span>与表达式</span><code>(P*)ps</code><span>指向类布局中不同的位置。因为函数</span><code>S::pvf</code><span>希望获得一个</span><code>S*</code><span>作为隐藏的</span><code>this</code><span>指针参数，虚函数必须把</span><code>R*</code><span>转化为 </span><code>S*</code><span>。因此，在</span><code>S</code><span>对</span><code>R</code><span>虚函数表的拷贝中，</span><code>pvf</code><span>函数对应的项，指向的是一个“</span><strong><span>调整块</span></strong><span>”的地址，该调整块使用必要的计算，把</span><code>R*</code><span>转换为需要的</span><code>S*</code><span>。</span></p><p><span>这就是</span><code>thunk1: this-= sdPR; goto S::pvf</code><span>干的事。先根据</span><code>P</code><span>和</span><code>R</code><span>在</span><code>S</code><span>中的偏移，调整</span><code>this</code><span>为</span><code>P*</code><span>，也就是</span><code>S*</code><span>，然后跳转到相应的虚函数处执行。</span><strong><span>在微软VC++实现中，对于有虚函数的多重继承，只有当派生类虚函数覆盖了多个基类的虚函数时，才使用调整块。</span></strong><span> </span></p><h4 id='地址点与逻辑this调整'><span>地址点与逻辑</span><code>this</code><span>调整</span></h4><p><span>考虑下一个虚函数</span><code>S::rvf()</code><span>，该函数覆盖了</span><code>R::rvf()</code><span>。我们都知道</span><code>S::rvf()</code><span>必须有一个隐藏的</span><code>S*</code><span>类型的</span><code>this</code><span>参数。但是，因为也可以用</span><code>R*</code><span>来调用</span><code>rvf()</code><span>，也就是说，</span><code>R</code><span>的</span><code>rvf</code><span>虚函数槽可能以如下方式被用到：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">((<span class="cm-variable">R</span><span class="cm-operator">*</span>)<span class="cm-variable">ps</span>)<span class="cm-operator">-&gt;</span><span class="cm-variable">rvf</span>(); <span class="cm-comment">// (*((R*)ps)-&gt;R::vfptr[1])((R*)ps) &nbsp;</span></span></pre></div></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 23px;"></div><div class="CodeMirror-gutters" style="display: none; height: 23px;"></div></div></div></pre><p><span>所以，大多数实现用另一个调整块将传递给</span><code>rvf</code><span>的</span><code>R*</code><span>转换为</span><code>S*</code><span>。还有一些实现在</span><code>S</code><span>的虚函数表末尾添加一个特别的虚函数项，该虚函数项提供方法，从而可以直接调用</span><code>ps-&gt;rvf()</code><span>，而不用先转换</span><code>R*</code><span>。MSC++的实现不是这样，MSC++有意将</span><code>S::rvf</code><span>编译为接受一个指向</span><code>S</code><span>中嵌套的</span><code>R</code><span>实例，而非指向</span><code>S</code><span>实例的指针（我们称这种行为是“给派生类的指针类型与该虚函数第一次被引入时接受的指针类型相同”）。所有这些在后台透明发生，对成员变量的存取，成员函数的</span><code>this</code><span>指针，都进行逻辑</span><code>this</code><span>调整。当然，在debugger中，必须对这种</span><code>this</code><span>调整进行补偿。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">ps</span><span class="cm-operator">-&gt;</span><span class="cm-variable">rvf</span>(); <span class="cm-comment">// ((R*)ps)-&gt;rvf(); // S::rvf((R*)ps) &nbsp;</span></span></pre></div></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 23px;"></div><div class="CodeMirror-gutters" style="display: none; height: 23px;"></div></div></div></pre><p><span>调用</span><code>rvf</code><span>虚函数时，直接给入</span><code>R*</code><span>作为</span><code>this</code><span>指针。所以，当覆盖非最左边的基类的虚函数时，MSC++一般不创建调整块，也不增加额外的虚函数项。</span></p><h4 id='调整块'><span>调整块</span></h4><p><span>正如已经描述的，有时需要调整块来调整</span><code>this</code><span>指针的值（</span><code>this</code><span>指针通常位于栈上返回地址之下，或者在寄存器中），在</span><code>this</code><span>指针上加或减去一个常量偏移，再调用虚函数。某些实现（尤其是基于</span><code>cfront</code><span>的）并不使用调整块机制。它们在每个虚函数表项中增加额外的偏移数据。每当虚函数被调用时，该偏移数据（通常为0）,被加到对象的地址上，然后对象的地址再作为this指针传入。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">ps</span><span class="cm-operator">-&gt;</span><span class="cm-variable">rvf</span>(); &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">// struct { void (*pfn)(void*); size_t disp; }; &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment">// (*ps-&gt;vfptr[i].pfn)(ps + ps-&gt;vfptr[i].disp); &nbsp;</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 69px;"></div><div class="CodeMirror-gutters" style="display: none; height: 69px;"></div></div></div></pre><p><span>当调用</span><code>rvf</code><span>虚函数时，前一句表示虚函数表每一项是一个结构，结构中包含偏移量；后一句表示调用第</span><code>i</code><span>个虚函数时，</span><code>this</code><span>指针使用保存在虚函数表中第i项的偏移量来进行调整。这种方法的缺点是虚函数表增大了，虚函数的调用也更加复杂。现代基于PC的实现一般采用“调整—跳转”技术：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">S::pvf</span><span class="cm-operator">-</span><span class="cm-variable">adjust</span>: <span class="cm-comment">// MSC++ &nbsp;</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">this</span> <span class="cm-operator">-=</span> <span class="cm-variable">SdPR</span>; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">goto</span> <span class="cm-variable">S::pvf</span>() &nbsp;</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 69px;"></div><div class="CodeMirror-gutters" style="display: none; height: 69px;"></div></div></div></pre><p><span>当然，下面的代码序列更好（然而，当前没有任何实现采用该方法）：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">S::pvf</span><span class="cm-operator">-</span><span class="cm-variable">adjust</span>: &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">this</span> <span class="cm-operator">-=</span> <span class="cm-variable">SdPR</span>; <span class="cm-comment">// fall into S::pvf() &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">S::pvf</span>() { ... } &nbsp;</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 69px;"></div><div class="CodeMirror-gutters" style="display: none; height: 69px;"></div></div></div></pre><p><span>IBM的C++编译器使用该方法。</span></p><h4 id='虚继承下的虚函数'><span>虚继承下的虚函数</span></h4><p><code>T</code><span>虚继承</span><code>P</code><span>，覆盖</span><code>P</code><span>的虚成员函数，声明了新的虚函数。如果采用在基类虚函数表末尾添加新项的方式，则访问虚函数总要求访问虚基类。在VC++中，为了避免获取虚函数表时，转换到虚基类P的高昂代价，</span><strong><code>T</code><span>中的新虚函数通过一个新的虚函数表 获取</span></strong><span>，从而带来了一个新的虚函数表指针。该指针放在</span><code>T</code><span>实例的顶端。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">T</span> : <span class="cm-keyword">virtual</span> <span class="cm-variable">P</span> { &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">int</span> <span class="cm-variable">t1</span>; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">void</span> <span class="cm-variable">pvf</span>(); &nbsp; &nbsp; &nbsp; &nbsp; <span class="cm-comment">// overrides P::pvf &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-keyword">virtual</span> <span class="cm-variable-3">void</span> <span class="cm-variable">tvf</span>(); <span class="cm-comment">// new &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">}; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">void</span> <span class="cm-def">T::pvf</span>() { &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-operator">++</span><span class="cm-variable">p1</span>; <span class="cm-comment">// ((P*)this)-&gt;p1++; // vbtable lookup! &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-operator">++</span><span class="cm-variable">t1</span>; <span class="cm-comment">// this-&gt;t1++; &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">} &nbsp;</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 207px;"></div><div class="CodeMirror-gutters" style="display: none; height: 207px;"></div></div></div></pre><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_1312878454a1Na.jpg" referrerpolicy="no-referrer" alt="img"></p><p><span>如上所示，</span><strong><span>即使是在虚函数中，访问虚基类的成员变量也要通过获取虚基类表的偏移，实行计算来进行。这样做之所以必要，是因为虚函数可能被进一步继承的类所覆盖，而进一步继承的类的布局中，虚基类的位置变化了。</span></strong><span>下面就是这样的一个类：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">U</span> : <span class="cm-variable">T</span> { &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable-3">int</span> <span class="cm-variable">u1</span>; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">}; &nbsp;</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 69px;"></div><div class="CodeMirror-gutters" style="display: none; height: 69px;"></div></div></div></pre><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_1312878510F137.jpg" referrerpolicy="no-referrer" alt="img"></p><p><span>在此</span><code>U</code><span>增加了一个成员变量，从而改变了</span><code>P</code><span>的偏移。因为VC++实现中，</span><code>T::pvf()</code><span>接受的是嵌套在</span><code>T</code><span>中的</span><code>P</code><span>的指针，所以，需要提供一个调整块，把</span><code>this</code><span>指针调整到</span><code>T::t1</code><span>之后（该处即是</span><code>P</code><span>在</span><code>T</code><span>中的位置）。</span></p><h4 id='特殊成员函数'><span>特殊成员函数</span></h4><p><span>本节讨论编译器合成到特殊成员函数中的隐藏代码。</span></p><h5 id='构造函数和析构函数'><span>构造函数和析构函数</span></h5><p><span>正如我们所见，在构造和析构过程中，有时需要初始化一些隐藏的成员变量。最坏的情况下，一个构造函数要执行如下操作：</span></p><ol start='' ><li><span>如果是“最终派生类”，初始化</span><code>vbptr</code><span>成员变量，调用虚基类的构造函数；</span></li><li><span>调用非虚基类的构造函数</span></li><li><span>调用成员变量的构造函数</span></li><li><span>初始化虚函数表成员变量</span></li><li><span>执行构造函数体中，程序所定义的其他初始化代码</span></li></ol><p><span>（注意：一个“最终派生类”的实例，一定不是嵌套在其他派生类实例中的基类实例）</span>
<span>所以，如果你有一个包含虚函数的很深的继承层次，即使该继承层次由单继承构成，对象的构造可能也需要很多针对虚函数表的初始化。反之，析构函数必须按照与构造时严格相反的顺序来“肢解”一个对象。</span></p><ol start='' ><li><span>合成并初始化虚函数表成员变量</span></li><li><span>执行析构函数体中，程序定义的其他析构代码</span></li><li><span>调用成员变量的析构函数（按照相反的顺序）</span></li><li><span>调用直接非虚基类的析构函数（按照相反的顺序）</span></li><li><span>如果是“最终派生类”，调用虚基类的析构函数（按照相反顺序）</span></li></ol><p><span>在VC++中，有虚基类的类的构造函数接受一个隐藏的“最终派生类标志”，标示虚基类是否需要初始化。对于析构函数，VC++采用“分层析构模型”，代码中加入一个隐藏的析构函数，该函数被用于析构包含虚基类的类（对于 “最终派生类”实例而言）；代码中再加入另一个析构函数，析构不包含虚基类的类。前一个析构函数调用后一个。</span></p><h5 id='虚析构函数与delete操作符'><span>虚析构函数与</span><code>delete</code><span>操作符</span></h5><p><span>假如</span><code>A</code><span>是</span><code>B</code><span>的父类，</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">A</span><span class="cm-operator">*</span> <span class="cm-variable">p</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">B</span>(); </span></pre></div></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 23px;"></div><div class="CodeMirror-gutters" style="display: none; height: 23px;"></div></div></div></pre><p><span>如果析构函数不是虚拟的，那么，你后面就必须这样才能安全的删除这个指针： </span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">delete</span> (<span class="cm-variable">B</span><span class="cm-operator">*</span>)<span class="cm-variable">p</span>; </span></pre></div></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 23px;"></div><div class="CodeMirror-gutters" style="display: none; height: 23px;"></div></div></div></pre><p><span>但如果构造函数是虚拟的，就可以在运行时动态绑定到</span><code>B</code><span>类的析构函数，直接： </span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">delete</span> <span class="cm-variable">p</span>; </span></pre></div></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 23px;"></div><div class="CodeMirror-gutters" style="display: none; height: 23px;"></div></div></div></pre><p><span>就可以了。这就是虚析构函数的作用。 实际上，很多人这样总结：当且仅当类里包含至少一个虚函数的时候才去声明虚析构函数。 考虑结构</span><code>V</code><span>和</span><code>W</code><span>。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">V</span> { &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-keyword">virtual</span> <span class="cm-variable">~V</span>(); &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">}; </span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 69px;"></div><div class="CodeMirror-gutters" style="display: none; height: 69px;"></div></div></div></pre><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_1312878556flHW.jpg" referrerpolicy="no-referrer" alt="img"></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">W</span> : <span class="cm-variable">V</span> { &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-keyword">operator</span> <span class="cm-keyword">delete</span>(); &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">}; &nbsp;</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 69px;"></div><div class="CodeMirror-gutters" style="display: none; height: 69px;"></div></div></div></pre><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/5419476_13128785986DxX.jpg" referrerpolicy="no-referrer" alt="img"></p><p><span>析构函数可以为虚。</span><strong><span>一个类如果有虚析构函数的话，将会象有其他虚函数一样，拥有一个虚函数表指针，虚函数表中包含一项，其内容为指向对该类适用的虚析构函数的地址。这些机制和普通虚函数相同。虚析构函数的特别之处在于：当类实例被销毁时，虚析构函数被隐含地调用。调用地（</span><code>delete</code><span>发生的地方）虽然不知道销毁的动态类型，然而，要保证调用对该类型合适的</span><code>delete</code><span>操作符。</span></strong><span>例如，当</span><code>pv</code><span>指向W的实例时，当</span><code>W::~W</code><span>被调用之后，</span><code>W</code><span>实例将由</span><code>W</code><span>类的</span><code>delete</code><span>操作符来销毁。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">V</span><span class="cm-operator">*</span> <span class="cm-variable">pv</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">V</span>; &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">delete</span> <span class="cm-variable">pv</span>; &nbsp; <span class="cm-comment">// pv-&gt;~V::V(); // use ::operator delete() &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pv</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">W</span>; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">delete</span> <span class="cm-variable">pv</span>; &nbsp; <span class="cm-comment">// pv-&gt;~W::W(); // use W::operator delete() 动态绑定到 W的析构函数，W默认的析构函数调用{delete this;} &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pv</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">W</span>; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">::<span class="cm-keyword">delete</span> <span class="cm-variable">pv</span>; <span class="cm-comment">// pv-&gt;~W::W(); // use ::operator delete() &nbsp;</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 138px;"></div><div class="CodeMirror-gutters" style="display: none; height: 138px;"></div></div></div></pre><p><code>V</code><span>没有定义</span><code>delete</code><span>操作符，</span><code>delete</code><span>时使用函数库的</span><code>delete</code><span>操作符；</span><br/><code>W</code><span>定义了</span><code>delete</code><span>操作符，</span><code>delete</code><span>时使用自己的</span><code>delete</code><span>操作符；</span><br/><span>可以用全局范围标示符显示地调用函数库的</span><code>delete</code><span>操作符。</span><br/><span>为了实现上述语意，VC++扩展了其“分层析构模型”，从而自动创建另一个隐藏的析构帮助函数——“deleting析构函数”，然后，用该函数的地址来替换虚函数表中“实际”虚析构函数的地址。析构帮助函数调用对该类合适的析构函数，然后为该类有选择性地调用合适的</span><code>delete</code><span>操作符。</span></p><h3 id='数组'><span>数组</span></h3><p><span>堆上分配空间的数组使虚析构函数进一步复杂化。问题变复杂的原因有两个：</span></p><ol start='' ><li><span>堆上分配空间的数组，由于数组可大可小，所以，数组大小值应该和数组一起保存。因此，堆上分配空间的数组会分配额外的空间来存储数组元素的个数；</span></li><li><span>当数组被删除时，数组中每个元素都要被正确地释放，即使当数组大小不确定时也必须成功完成该操作。然而，派生类可能比基类占用更多的内存空间，从而使正确释放比较困难。</span></li></ol><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">WW</span> : <span class="cm-variable">W</span> { <span class="cm-variable-3">int</span> <span class="cm-variable">w1</span>; }; &nbsp;</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pv</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">W</span>[<span class="cm-variable">m</span>]; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">delete</span> [] <span class="cm-variable">pv</span>; <span class="cm-comment">// delete m W's (sizeof(W) == sizeof(V)) &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pv</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">WW</span>[<span class="cm-variable">n</span>]; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">delete</span> [] <span class="cm-variable">pv</span>; <span class="cm-comment">// delete n WW's (sizeof(WW) &gt; sizeof(V)) </span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 115px;"></div><div class="CodeMirror-gutters" style="display: none; height: 115px;"></div></div></div></pre><p><code>WW</code><span>从</span><code>W</code><span>继承，增加了一个成员变量，因此，</span><code>WW</code><span>占用的内存空间比</span><code>W</code><span>大。然而，不管指针</span><code>pv</code><span>指向</span><code>W</code><span>的数组还是</span><code>WW</code><span>的数组，</span><code>delete[]</code><span>都必须正确地释放</span><code>WW</code><span>或</span><code>W</code><span>对象占用的内存空间。</span></p><p><span>虽然从严格意义上来说，数组</span><code>delete</code><span>的多态行为C++标准并未定义，然而，微软有一些客户要求实现该行为。因此，在MSC++中，该行为是用另一个编译器生成的虚析构帮助函数来完成。该函数被称为“向量</span><code>delete</code><span>析构函数”（因其针对特定的类定制，比如</span><code>WW</code><span>，所以，它能够遍历数组的每个元素，调用对每个元素适用的析构函数）。</span></p><h3 id='异常处理'><span>异常处理</span></h3><p><span>简单说来，异常处理是C++标准委员会工作文件提供的一种机制，通过该机制，一个函数可以通知其调用者“异常”情况的发生，调用者则能据此选择合适的代码来处理异常。该机制在传统的“函数调用返回，检查错误状态代码”方法之外，给程序提供了另一种处理错误的手段。</span></p><p><span>因为C++是面向对象的语言，很自然地，C++中用对象来表达异常状态。并且，使用何种异常处理也是基于“抛出的”异常对象的静态或动态类型来决定的。不光如此，既然C++总是保证超出范围的对象能够被正确地销毁，异常实现也必须保证当控制从异常抛出点转换到异常“捕获”点时（栈展开），超出范围的对象能够被自动、正确地销毁。考虑如下例子：</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">X</span> { <span class="cm-variable">X</span>(); }; <span class="cm-comment">// exception object class &nbsp;</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">struct</span> <span class="cm-def">Z</span> { <span class="cm-variable">Z</span>(); <span class="cm-variable">~Z</span>(); }; <span class="cm-comment">// class with a destructor &nbsp;</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">extern</span> <span class="cm-variable-3">void</span> <span class="cm-def">recover</span>(<span class="cm-keyword">const</span> <span class="cm-variable">X</span><span class="cm-operator">&amp;</span>); &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">void</span> <span class="cm-def">f</span>(<span class="cm-variable-3">int</span>), <span class="cm-variable">g</span>(<span class="cm-variable-3">int</span>); &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">int</span> <span class="cm-def">main</span>() { &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-keyword">try</span> { &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp;<span class="cm-variable">f</span>(<span class="cm-number">0</span>); &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; } <span class="cm-keyword">catch</span> (<span class="cm-keyword">const</span> <span class="cm-variable">X</span><span class="cm-operator">&amp;</span> <span class="cm-variable">rx</span>) { &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp;<span class="cm-variable">recover</span>(<span class="cm-variable">rx</span>); &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; } &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-keyword">return</span> <span class="cm-number">0</span>; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">} &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">void</span> <span class="cm-def">f</span>(<span class="cm-variable-3">int</span> <span class="cm-variable">i</span>) { &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable">Z</span> <span class="cm-variable">z1</span>; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable">g</span>(<span class="cm-variable">i</span>); &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable">Z</span> <span class="cm-variable">z2</span>; &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-variable">g</span>(<span class="cm-variable">i</span><span class="cm-operator">-</span><span class="cm-number">1</span>); &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">} &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable-3">void</span> <span class="cm-def">g</span>(<span class="cm-variable-3">int</span> <span class="cm-variable">j</span>) { &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; <span class="cm-keyword">if</span> (<span class="cm-variable">j</span> <span class="cm-operator">&lt;</span> <span class="cm-number">0</span>) &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp; &nbsp;<span class="cm-keyword">throw</span> <span class="cm-variable">X</span>(); &nbsp;</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">} &nbsp;</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 506px;"></div><div class="CodeMirror-gutters" style="display: none; height: 506px;"></div></div></div></pre><p><code>X</code><span>是异常类，</span><code>Z</code><span>是带析构函数的工作类，</span><code>recover</code><span>是错误处理函数，</span><code>f</code><span>和</span><code>g</code><span>一起产生异常条件，</span><code>g</code><span>实际抛出异常。</span></p><p><span>这段程序会抛出异常。在</span><code>main</code><span>中，加入了处理异常的</span><code>try &amp; catch</code><span>框架，当调用</span><code>f(0)</code><span>时，</span><code>f</code><span>构造</span><code>z1</code><span>，调用</span><code>g(0)</code><span>后，再构造</span><code>z2</code><span>，再调用</span><code>g(-1)</code><span>，此时</span><code>g</code><span>发现参数为负，抛出</span><code>X</code><span>异常对象。我们希望在某个调用层次上，该异常能够得到处理。既然</span><code>g</code><span>和</span><code>f</code><span>都没有建立处理异常的框架，我们就只能希望</span><code>main</code><span>函数建立的异常处理框架能够处理X异常对象。实际上，确实如此。当控制被转移到</span><code>main</code><span>中异常捕获点时，从</span><code>g</code><span>中的异常抛出点到</span><code>main</code><span>中的异常捕获点之间，该范围内的对象都必须被销毁。在本例中，</span><code>z2</code><span>和</span><code>z1</code><span>应该被销毁。</span>
<span>谈到异常处理的具体实现方式，一般情况下，在抛出点和捕获点都使用“表”来表述能够捕获异常对象的类型；并且，实现要保证能够在特定的捕获点真正捕获特定的异常对象；一般地，还要运用抛出的对象来初始化捕获语句的“实参”。通过合理地选择编码方案，可以保证这些表格不会占用过多的内存空间。</span>
<span>异常处理的开销到底如何？让我们再考虑一下函数</span><code>f</code><span>。看起来f没有做异常处理。</span><code>f</code><span>确实没有包含</span><code>try</code><span>，</span><code>catch</code><span>，或者是</span><code>throw</code><span>关键字，因此，我们会猜异常处理应该对f没有什么影响。错！编译器必须保证一旦</span><code>z1</code><span>被构造，而后续调用的任何函数向f抛回了异常，异常又出了</span><code>f</code><span>的范围时，</span><code>z1</code><span>对象能被正确地销毁。同样，一旦</span><code>z2</code><span>被构造，编译器也必须保证后续抛出异常时，能够正确地销毁</span><code>z2</code><span>和</span><code>z1</code><span>。</span>
<span>要实现这些“展开”语意，编译器必须在后台提供一种机制，该机制在调用者函数中，针对调用的函数抛出的异常动态决定异常环境（处理点）。这可能包括在每个函数的准备工作和善后工作中增加额外的代码，在最糟糕的情况下，要针对每一套对象初始化的情况更新状态变量。例如，上述例子中，</span><code>z1</code><span>应被销毁的异常环境当然与</span><code>z2</code><span>和</span><code>z1</code><span>都应该被销毁的异常环境不同，因此，不管是在构造</span><code>z1</code><span>后，还是继而在构造</span><code>z2</code><span>后，VC++都要分别在状态变量中更新（存储）新的值。</span>
<span>所有这些表，函数调用的准备和善后工作，状态变量的更新，都会使异常处理功能造成可观的内存空间和运行速度开销。正如我们所见，即使在没有使用异常处理的函数中，该开销也会发生。</span>
<span>幸运的是，一些编译器可以提供编译选项，关闭异常处理机制。那些不需要异常处理机制的代码，就可以避免这些额外的开销了。</span></p><p><span>好了，现在你可以写C++编译器了（开个玩笑）。</span>
<span>在本文中，我们讨论了许多重要的C++运行实现问题。我们发现，很多美妙的C++语言特性的开销很低，同时，其他一些美妙的特性（译者注：主要是和“虚”字相关的东西）将造成较大的开销。C++很多实现机制都是在后台默默地为你工作。一般说来，单独看一段代码时，很难衡量这段代码造成的运行时开销，必须把这段代码放到一个更大的环境中来考察，运行时开销问题才能得到比较明确的答案。</span></p><p><span>以上引用</span><a href='https://blog.csdn.net/weixin_42665418/article/details/81066891'><span>C++继承中的内存布局</span></a></p><h2 id='继承权限'><span>继承权限</span></h2><p><span>在c++的继承控制中，有三种不同的控制权限，分别是</span><code>public</code><span>、</span><code>protected</code><span>和</span><code>private</code><span>。定义派生类时，若不显示加上这三个关键字，就会使用默认的方式，用</span><code>struct</code><span>定义的类是默认</span><code>public</code><span>继承，</span><code>class</code><span>定义的类是默认</span><code>private</code><span>继承。这和Java有很大的不同，Java默认使用</span><code>public</code><span>继承，而且只有公有继承。</span></p><ol start='' ><li><p><span>使用</span><code>public</code><span>继承时，派生类内部可以访问基类中</span><code>public</code><span>和</span><code>protected</code><span>成员，但是类外只能通过派生类的对象访问基类的</span><code>public</code><span>成员。</span><br/><span>（1）基类的public成员在派生类中依然是</span><code>public</code><span>的。</span><br/><span>（2）基类中的</span><code>protected</code><span>成员在派生类中依然是</span><code>protected</code><span>的。</span><br/><span>（3）基类中的</span><code>private</code><span>成员在派生类中不可访问。</span></p></li><li><p><span>使用</span><code>protected</code><span>继承时，派生类内部可以访问基类中</span><code>public</code><span>和</span><code>protected</code><span>成员，并且类外也不能通过派生类的对象访问基类的成员（可以在派生类中添加公有成员函数接口间接访问基类中的</span><code>public</code><span>和</span><code>protected</code><span>成员）</span></p><p><span>（1）基类的</span><code>public</code><span>成员在派生类中变为</span><code>protected</code><span>成员。</span><br/><span>（2）基类的</span><code>protected</code><span>成员在派生类中依然是</span><code>protected</code><span>成员。</span><br/><span>（3）基类中的</span><code>private</code><span>成员在派生类中不可访问。  </span></p></li><li><p><span>使用</span><code>private</code><span>继承时，派生类内部可以访问基类中</span><code>public</code><span>和</span><code>protected</code><span>成员，并且类外也不能通过派生类的对象访问基类的成员（可以在派生类中添加公有成员函数接口间接访问基类中的</span><code>public</code><span>和</span><code>protected</code><span>成员）</span><br/><span>（1）基类的</span><code>public</code><span>成员在派生类中变成</span><code>private</code><span>成员。</span><br/><span>（2）基类的</span><code>protected</code><span>成员在派生类中变成</span><code>private</code><span>成员。</span><br/><span>（3）基类的</span><code>private</code><span>成员在派生类中不可访问。</span></p><p><span>为了便于理解，我们用一个表格来说明这几种控制符使用的情况： </span></p></li></ol><figure><table><thead><tr><th style='text-align:center;' ><span>派生方式</span></th><th style='text-align:center;' ><span>基类的</span><code>public</code><span>成员</span></th><th style='text-align:center;' ><span>基类的</span><code>protected</code><span>成员</span></th><th style='text-align:center;' ><span>基类的</span><code>private</code><span>成员</span></th></tr></thead><tbody><tr><td style='text-align:center;' ><code>public</code><span>派生</span></td><td style='text-align:center;' ><span>还是</span><code>public</code><span>成员</span></td><td style='text-align:center;' ><span>还是</span><code>protected</code><span>成员</span></td><td style='text-align:center;' ><span>不可见</span></td></tr><tr><td style='text-align:center;' ><code>protected</code><span>派生</span></td><td style='text-align:center;' ><span>变成</span><code>protected</code><span>成员</span></td><td style='text-align:center;' ><span>还是</span><code>protected</code><span>成员</span></td><td style='text-align:center;' ><span>不可见</span></td></tr><tr><td style='text-align:center;' ><code>private</code><span>派生</span></td><td style='text-align:center;' ><span>变成</span><code>private</code><span>成员</span></td><td style='text-align:center;' ><span>变成</span><code>private</code><span>成员</span></td><td style='text-align:center;' ><span>不可见</span></td></tr></tbody></table></figure><p><span>以上引用</span><a href='https://blog.csdn.net/weixin_28712713/article/details/80967650'><span>C++ 公有继承、保护继承和私有继承的对比详解</span></a></p><p><span>注意：</span><strong><span>使用 </span><code>using</code><span> 关键字可以改变基类成员在派生类中的访问权限，例如将 </span><code>public</code><span> 改为 </span><code>private</code><span>、将 </span><code>protected</code><span> 改为 </span><code>public</code><span>，</span><code>using</code><span> 只能改变基类中 </span><code>public</code><span> 和 </span><code>protected</code><span> 成员的访问权限，不能改变 </span><code>private</code><span> 成员的访问权限，因为基类中 </span><code>private</code><span> 成员在派生类中是不可见的，根本不能使用，所以基类中的 </span><code>private</code><span> 成员在派生类中无论如何都不能访问。</span></strong></p><p><span>以上引用</span><a href='http://c.biancheng.net/view/2269.html'><span>C++三种继承方式</span></a></p><h2 id='多继承'><span>多继承</span></h2><p><span> 一个类有多个基类,这样的继承关系称为多继承;</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">派生类名</span> : <span class="cm-variable">访问控制</span> <span class="cm-variable">基类名1</span>, <span class="cm-variable">访问控制</span> <span class="cm-variable">基类名2</span>, ...</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">{</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> &nbsp; &nbsp;<span class="cm-variable">数据成员和成员函数声明</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">};</span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 92px;"></div><div class="CodeMirror-gutters" style="display: none; height: 92px;"></div></div></div></pre><p><span>类 </span><code>C</code><span> 可以根据访问控制同时继承类 </span><code>A</code><span> 和类 </span><code>B</code><span> 的成员，并添加自己的成员:</span></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/1382048-20180509223417307-556498045.png" referrerpolicy="no-referrer" alt="img"></p><ol start='' ><li><span>多个基类的派生类构造函数可以用初始化列表调用基类构造函数来初始化数据成员</span></li><li><span>执行顺序与单继承构造函数情况类似。多个直接基类构造函数执行顺序取决于定义派生类时指定的各个继承基类的顺序</span></li><li><span>一个派生类对象拥有多个直接或间接基类的成员。不同名成员访问不会出现二义性。如果不同的基类有同名成员，派生类对象访问时应加以识别</span></li></ol><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/1382048-20180509224828244-2086729123.png" referrerpolicy="no-referrer" alt="img"></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/1382048-20180509224833612-467652937.png" referrerpolicy="no-referrer" alt="img"></p><p><span>以上引用</span><a href='https://www.cnblogs.com/jimodetiantang/p/9017191.html'><span>C++之多继承与虚继承</span></a></p><h2 id='虚继承'><span>虚继承</span></h2><p><span>如果一个派生类从多个基类派生，而这些基类又有一个共同的基类，则在对该基类中声明的名字进行访问时，可能产生二义性。 如下图：</span></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/1382048-20180509225219586-185038967.png" referrerpolicy="no-referrer" alt="img"></p><p><span>如下图的分析：</span></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/1382048-20180509225317221-165410554.png" referrerpolicy="no-referrer" alt="img"></p><ol start='' ><li><span>如果一个派生类从多个基类派生，而这些基类又有一个共同的基类，则在对该基类中声明的名字进行访问时，可能产生二义性。</span></li><li><span>如果在多条继承路径上有一个公共的基类，那么在继承路径的某处汇合点，这个公共基类就会在派生类的对象中产生多个基类子对象。</span></li><li><span>要使这个公共基类在派生类中只产生一个子对象，必须对这个基类声明为虚继承，使这个基类成为虚基类</span></li><li><span>虚继承声明使用关键字 </span><code>virtual</code></li></ol><p><span>如下虚继承示例：</span></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/1382048-20180509225605495-1061567067.png" referrerpolicy="no-referrer" alt="img"></p><p><img src="https://gitee.com/zyuegege/images/raw/master/imgs/1382048-20180509225612559-284349121.png" referrerpolicy="no-referrer" alt="img"></p><p><span>以上引用</span><a href='https://www.cnblogs.com/jimodetiantang/p/9017191.html'><span>C++之多继承与虚继承</span></a></p><h2 id='接口继承实现继承'><span>接口继承、实现继承</span></h2><p><span>所谓接口继承，就是派生类只继承函数的接口，也就是声明；而实现继承，就是派生类同时继承函数的接口和实现。我们都很清楚C++中有几个基本的概念，虚函数、纯虚函数、非虚函数。</span></p><ol start='' ><li><span>虚函数</span></li></ol><p><span>虚函数是指一个类中你希望重载的成员函数，当你用一个基类指针或引用指向一个继承类对象的时候，你调用一个虚函数，实际调用的是继承类的版本。——MSDN</span></p><p><span>虚函数用来表现基类和派生类的成员函数之间的一种关系.</span><br/><span>虚函数的定义在基类中进行,在需要定义为虚函数的成员函数的声明前冠以关键字 virtual.</span><br/><span>基类中的某个成员函数被声明为虚函数后,此虚函数就可以在一个或多个派生类中被重新定义.</span><br/><span>在派生类中重新定义时,其函数原型,包括返回类型,函数名,参数个数,参数类型及参数的先后顺序,都必须与基类中的原型完全相同.</span><br/><span>虚函数是重载的一种表现形式,是一种动态的重载方式.</span></p><ol start='2' ><li><span>纯虚函数</span></li></ol><p><span>纯虚函数在基类中没有定义，它们被初始化为0。</span><br/><span>任何用纯虚函数派生的类，都要自己提供该函数的具体实现。</span><br/><span>定义纯虚函数  </span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="c++"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 9.5px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">virtual</span> <span class="cm-variable-3">void</span> <span class="cm-def">fun</span>(<span class="cm-variable-3">void</span>) <span class="cm-operator">=</span> <span class="cm-number">0</span>;</span></pre></div></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 23px;"></div><div class="CodeMirror-gutters" style="display: none; height: 23px;"></div></div></div></pre><p><span>至于为什么要定义这些函数，我们可以将虚函数、纯虚函数和非虚函数的功能与接口继承与实现继承联系起来：</span></p><p><strong><span>声明一个纯虚函数（pure virtual）的目的是为了让派生类只继承函数接口，也就是上面说的接口继承。</span></strong><span>纯虚函数一般是在不方便具体实现此函数的情况下使用。也就是说基类无法为继承类规定一个统一的缺省操作，但继承类又必须含有这个函数接口，并对其分别实现。但是，在C++中，我们是可以为纯虚函数提供定义的，只不过这种定义对继承类来说没有特定的意义。因为继承类仍然要根据各自需要实现函数。通俗说，纯虚函数就是要求其继承类必须含有该函数接口，并对其进行实现。是对继承类的一种接口实现要求，但并不提供缺省操作，各个继承类必须分别实现自己的操作。</span></p><p><strong><span>声明非纯虚函数（impure virtual）的目的是让继承类继承该函数的接口和缺省实现。</span></strong><span>与纯虚函数唯一的不同就是其为继承类提供了缺省操作，继承类可以不实现自己的操作而采用基类提供的默认操作。</span></p><p><strong><span>声明非虚函数（non-virtual）的目的是为了令继承类继承函数接口及一份强制性实现。</span></strong><span>相对于虚函数来说，非虚函数对继承类要求的更为严格，继承类不仅要继承函数接口，而且也要继承函数实现。也就是为继承类定义了一种行为。</span></p><p><strong><span>纯虚函数</span></strong><span>：要求继承类必须含有某个接口，并对接口函数实现。</span><br/><strong><span>虚函数</span></strong><span>：继承类必须含有某个接口，可以自己实现，也可以不实现，而采用基类定义的缺省实现。</span><br/><strong><span>非虚函数</span></strong><span>：继承类必须含有某个接口，必须使用基类的实现。</span></p><p><span>虚函数在派生类中重新定义时候，如果返回的是对基类型的引用或是指针，那么派生类的虚函数可以返回基类函数返回类型的派生类引用或指针，而不是一定要求一样的，primer上看到的。</span></p><p><span>以上引用</span><a href='https://blog.csdn.net/ljinddlj/article/details/1922189'><span>[C++]接口继承与实现继承</span></a></p><hr /><h1 id='抽象基类'><span>抽象基类</span></h1><p><span>有时候我们需要表示一种抽象的概念，但是具体的实现方式可以有很多种，这个时候我们可以使用纯虚函数定义这种抽象概念，含有或未经覆盖直接继承纯虚函数的类时抽象基类。抽象基类负责定义接口，我们不能直接创建一个抽象基类的对象，但是可以创建派生类的对象，前提是这些类覆盖了纯虚函数。</span></p><p><span>通过在虚函数声明处分号之前添加=0将虚函数定义为纯虚函数，纯虚函数可以有定义，但是函数体必须定义在类的外部。</span>
<span>以上引用</span><a href='https://blog.csdn.net/xiongya8888/article/details/89578189'><span>C++抽象基类</span></a></p><hr /><h1 id='关于this指针的理解'><span>关于</span><code>this</code><span>指针的理解</span></h1><p><span>先要理解</span><code>class</code><span>的意思。</span><code>class</code><span>应该理解为一种类型，象</span><code>int</code><span>,</span><code>char</code><span>一样，是用户自定义的类型。用这个类型可以来声明一个变量，比如</span><code>int x</code><span>, </span><code>myclass my</code><span>等等。这样就像变量</span><code>x</code><span>具有</span><code>int</code><span>类型一样，变量</span><code>my</code><span>具有</span><code>myclass</code><span>类型。理解了这个，就好解释</span><code>this</code><span>了，</span><code>my</code><span>里的</span><code>this</code><span> 就是指向</span><code>my</code><span>的指针。如果还有一个变量</span><code>myclass mz</code><span>，</span><code>mz</code><span>的</span><code>this</code><span>就是指向</span><code>mz</code><span>的指针。 这样就很容易理解</span><code>this</code><span> 的类型应该是</span><code>myclass *</code><span>,而对其的解引用</span><code>*this</code><span>就应该是一个</span><code>myclass</code><span>类型的变量。通常在</span><code>class</code><span>定义时要用到类型变量自身时，因为这时候还不知道变量名（为了通用也不可能固定实际的变量名），就用</span><code>this</code><span>这样的指针来使用变量自身。 </span></p><ol start='' ><li><strong><span>this指针的用处: </span></strong><span>一个对象的this指针并不是对象本身的一部分，不会影响</span><code>sizeof(对象)</code><span>的结果。</span><code>this</code><span>作用域是在类内部，当在类的非静态成员函数中访问类的非静态成员的时候，编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说，即使你没有写上</span><code>this</code><span>指针，编译器在编译的时候也是加上</span><code>this</code><span>的，它作为非静态成员函数的隐含形参，对各成员的访问均通过</span><code>this</code><span>进行。例如，调用</span><code>date.SetMonth(9)</code><span> &lt;===&gt; </span><code>SetMonth(&amp;date, 9)</code><span>，</span><code>this</code><span>帮助完成了这一转换 .在成员函数内部，我们可以直接使用调用该函数的对象的成员，而无需通过成员访问运算符来做到这一点，因为</span><code>this</code><span>所指的正是这个对象。任何对类成员的直接访问都被看成</span><code>this</code><span>的隐式使用。</span><code>this</code><span>的目的总是指向这个对象，所以</span><code>this</code><span>是一个常量指针，我们不允许改变</span><code>this</code><span>中保存的地址.</span></li><li><strong><span>this指针的使用:</span></strong><span>一种情况就是，在类的非静态成员函数中返回类对象本身的时候，直接使用 </span><code>return *this；</code><span>另外一种情况是当参数与成员变量名相同时，如</span><code>this-&gt;n = n</code><span> （不能写成</span><code>n = n</code><span>）。</span></li></ol><p><strong><span>关于</span></strong><code>this</code><strong><span>指针的一个精典回答:</span></strong></p><p><span>当你进入一个房子后，你可以看见桌子、椅子、地板等，但是房子你是看不到全貌了。对于一个类的实例来说，你可以看到它的成员函数、成员变量，但是实例本身呢？</span><code>this</code><span>是一个指针，它时时刻刻指向你这个实例本身。</span></p><p><span>以上引用</span><a href='https://blog.csdn.net/chenyt01/article/details/51316022'><span>C++类中this指针的理解</span></a></p></div></div>

<script>(function(){var e=document.body.parentElement,t=[],n=null,i=document.body.classList.contains("typora-export-collapse-outline"),r=function(e,t,n){document.addEventListener(e,function(e){if(!e.defaultPrevented)for(var i=e.target;i&&i!=this;i=i.parentNode)if(i.matches(t)){!1===n.call(i,e)&&(e.preventDefault(),e.stopPropagation());break}},!1)};function o(){return e.scrollTop}r("click",".outline-expander",function(e){var t=this.closest(".outline-item-wrapper").classList;return t.contains("outline-item-open")?t.remove("outline-item-open"):t.add("outline-item-open"),d(),!1}),r("click",".outline-item",function(e){var t=this.querySelector(".outline-label");if(location.hash="#"+t.getAttribute("href"),i){var n=this.closest(".outline-item-wrapper").classList;n.contains("outline-item-open")||n.add("outline-item-open"),c(),n.add("outline-item-active")}});var a,s,l=function(){var e=o();n=null;for(var i=0;i<t.length&&t[i][1]-e<60;i++)n=t[i]},c=function(){document.querySelectorAll(".outline-item-active").forEach(e=>e.classList.remove("outline-item-active")),document.querySelectorAll(".outline-item-single.outline-item-open").forEach(e=>e.classList.remove("outline-item-open"))},d=function(){if(n){c();var e=document.querySelector('.outline-label[href="#'+(CSS.escape?CSS.escape(n[0]):n[0])+'"]');if(e)if(i){var t=e.closest(".outline-item-open>ul>.outline-item-wrapper");if(t)t.classList.add("outline-item-active");else{for(var r=(e=e.closest(".outline-item-wrapper")).parentElement.closest(".outline-item-wrapper");r;)r=(e=r).parentElement.closest(".outline-item-wrapper");e.classList.add("outline-item-active")}}else e.closest(".outline-item-wrapper").classList.add("outline-item-active")}};window.addEventListener("scroll",function(e){a&&clearTimeout(a),a=setTimeout(function(){l(),d()},300)});var u=function(){s=setTimeout(function(){!function(){t=[];var e=o();document.querySelector("#write").querySelectorAll("h1, h2, h3, h4, h5, h6").forEach(n=>{var i=n.getAttribute("id");t.push([i,e+n.getBoundingClientRect().y])})}(),l(),d()},300)};window.addEventListener("resize",function(e){s&&clearTimeout(s),u()}),u()})();</script><script src="/js/typora-convert.js"></script>
</body>
</html>